20 Advanced Coding Tips For Big Unity Projects

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so you finally decided to Begin work on your dream game a fantasy MMORPG sandbox Battle Royale powered by a blockchain economy what could possibly go wrong then two months later progress comes to a grinding halt you have scripts that are a thousand lines long you've forgotten what your old code does adding new features means you have to rewrite three old ones every script relies on every other one and overall your project becomes an unorganized unmanageable confusing dumpster fire of spaghetti code tragically you are forced to scrap the project and give up on your Game Dev dreams sound familiar there are hundreds of hours of unity tutorials online but very few are geared towards more advanced developers aiming to create large-scale commercial games that's why I've compiled a list of some of the most valuable Unity coding tips that I've learned over the years along with examples of how I've actually used these techniques in my own game hopefully by the end of this video you'll have the tool tools you need to write scalable well-structured clean code that won't come back to bite you down the road I've ordered these roughly by difficulty so stay till the end for the most advanced techniques let's start off with the simple one variable names many new programmers get way too excited when they realize that they can name variables whatever they want in case this is you consider that when you come back to a script after a few weeks there's no way you're going to remember what e and sussie and your mom mean variable names should be concise but meaningful and function names should be verbs if you really want to go above and beyond consider following some sort of strict naming convention I decided to use the standards outlined in the c-sharp documentation public fields are Pascal case private instance variables start with an underscore and our camel case and constants are in all caps you can also Define your own rules and conventions if they make sense in your project for example I used camel case for variables exposed to the unity editor and since I'm making a multiplayer game I've decided to start all networked variables with the prefix net the key here is consistency especially if you're working with a team moving on to equally groundbreaking advice comments use them frugally but definitely use them use them to describe the purpose of complicated instance variables explain confusing blocks of code or remind yourself of things to implement later do not use them to explain things that are obvious and do not use them to hoard old code for weeks on end that's what version control is for comments are especially useful for explaining the purpose of functions this is something I picked up from my University programming class the professor insists that every function should have a comment summarizing that method on public methods consider using documentation comments which you can use to provide pre-slash post conditions a description of parameters and an explanation of the return value continuing with code readability encapsulate code in functions wherever possible instead of having methods that are 100 lines long and if statements nested five times break up your logic into functions and break up those functions into more functions this helps to maintain a level of abstraction so that anyone can tell what your program is doing at the highest level then if you want to get into the weeds you can look at the implementation of one function at a time I used to pile all my logic into the update function and I would put a comment above each block of code explaining what it did if you use well-named functions you shouldn't need comments to explain what your code is doing plan out your code before you write it you can do this with a good old-fashioned paper and pen in a Word document or even directly in your IDE with comments I'm talking diagrams flow charts pseudo code whatever floats your boat trust me the time saved from debugging and rewriting code will be worth a little extra forethought in the end sometimes you'll need access to a variable in a separate script you may be used to Simply making this variable public but this means that the value can be changed by any script in your project leaving this variable open to external modification can cause logic errors and cause the class to behave in unexpected ways for example there's a reason why c-sharp doesn't let us modify the count variable of a list the standard solution is to make the variable private and then add a public function which Returns the variable's value called a getter c-sharp has a shorthand way to write these Getters and Setters if you want a variable to be publicly accessible but not publicly alterable make the setter private and the getter public a similar problem arises when we want to be able to set a variable from the inspector most tutorials will tell you to just make this variable public as we just discussed making a variable public can cause other issues luckily you can use a tag called serialize field instead this will allow you to access the variable in the inspector but not from other classes it will also clean up your intellisense Unity is built around a component architecture so it makes sense to write our code like components as well this means instead of having one script called player which handles everything having to do with the player object you should make a new script for each new feature system for example in my game I have six scripts on my player object a separate one for input movement Health weapon handling grenade throwing and UI each script works almost entirely separately from the others ideally you should be able to add and remove these components freely without impacting the other features if you absolutely need to reference other systems you can use the require component tag to ensure that that script will always exist on the object additionally it's best to design these components so that they work as intended on any game object for example I wrote my health class in a way that allows me to add it to any game object that has help such as barrels or drones in the future this is one of the main advantages of the component architecture enums can be used to represent a closed set of choices for example I use them to represent the weapon classes in my game and also to represent the current state of the match the coolest part is you can set the value of an enum inside the inspector with a drop down menu in order to use an enum declare the possible values in a list like this then you can make variables of that type which store one of the choices you specified co-routines always seemed very scary to me but now that I know how they work I found them to be super useful basically co-routines spread a task over multiple frames allowing you to run code with delays without blocking the main execution of your program they are most useful for methods that contain a sequence of events or a procedural animation please note Co routines still run on the main thread so using them is not the same as threading I use a CO routine to play my match start sequence since it involves a lot of sequential time delays I also use a co-routine to repeatedly Place footsteps underneath my characters to declare a co-routine create a function with a return type I enumerator then inside the function you can use yield return to pause the execution and continue in the same spot later on yield return new weight for seconds delays for a certain amount of time and yield return new wait until delays until the specified condition is true this is a great way to avoid a lot of messy timer with time.deltatime you can start a co-outine by calling start Co routine and passing in the function name as a string if you want to call a function after a certain one-time time delay then it may be easier to use the invoke method basically you can call this and pass in your function name as a string parameter along with a time in seconds this will call your method after the specified delay you can also use invoke repeating which will call a method repeatedly at certain time intervals I don't need the bots in my game to calculate a new destination every frame so I use invoke repeating to update their path every half a second instead structs are kind of like a light version of classes a key difference between structs and classes is that a struct is a value type while classes are reference types in this way structs behave similar to Primitives every time you assign a struct to a variable that struct is copied you should use a struct to store immutable data types that logically represent only a single value a lot of the time I use a struct when I want to have a list of some custom data types show up in the inspector for example I have a struct defined in my player UI manager which represents a UI group if I Mark the struct as serializable then I can set the fields of each UI group in the inspector I also use a structure to store and transmit input for the current frame since only the input Handler class should be able to change the input now we're getting to the good stuff a Singleton is a type of class which only allows one instance of itself to ever be created to implement a Singleton make a static variable of the same type as the class then in the awake method store the new instance into the static variable or destroy the script if the instance has already been assigned if you don't know what static variables are there are variables that belong to the whole class instead of just one object this means that you can access static members from anywhere with just the class name hey that's pretty convenient the issue is non-static members cannot be used in static functions a Singleton gives us the best of both worlds if you use the static instance variable to reference the actual instance of the script in the scene then you can access all the non-static members as if they were static Singletons are most suited for scripts that need to exist within the unity scene but also need to be referenced by many different scripts and remember there can only be one instance of the script in the scene at a time so don't try to use a Singleton for your enemy script since there will likely be many enemies in your scene at one time I use Singletons for my manager scripts like my game manager and network manager I also use it for utilities like my object pool the object pool needs to be in the scene but I also want to be able to reference it from anywhere as part of this tip I'm going to suggest that you create a manager class anytime you have a bunch of different scripts that need to access a set of information for example the current game State and the list of players on each team is important for many different scripts so that's why I created a Singleton game manager class to handle these systems and expose the information to anyone who needs it just remember that these managers should still only have one job each and should not be trying to handle too much at once in-game development much of our code is event driven this means most of the time we're just sitting around waiting for something to happen c-sharp events are well suited for this type of programming and they can help remove dependency between systems in my game the player movement script and the grenade script need to know when the match begins in order to reset their respective action cooldowns but the game manager handles the start of the match how should I solve this problem well I could Loop through every player in the match and then get a reference to the player movement and throw grenade script and then call a public function reset cooldown on each this would work but it makes my manager script dependent on the player scripts meaning I can't change my player scripts without changing the manager script let's use c-sharp events to solve this problem first I'll make a variable of type action inside the game manager called game started to trigger this event I'll call Dot invoke on that action variable I use a question mark before the dot operator to avoid null reference exceptions you can also add parameters like this but I don't need parameters in this case now my player script can subscribe to this event by referencing the action variable and then using plus equals to link its own function to that event now when the game started event is invoked by the game manager all the scripts which I've hooked into this action will be notified this means that the game manager does not have to deal with my player scripts at all and best of all if another script needs to reference this event in the future then I won't have to change my game manager to include it in general it's okay if your scripts depend on your manager classes but not the other way around I also use c-sharp events for my health quests I have an event for when the health is changed and for when the object has died this allows me to separate the health and regen logic from stuff like UI and death logic which is handled in a different script now I can use Health as a component that can be reused and added to any object Unity has its own event system which you may prefer it works in almost the same way as a c-sharp event except you define a Unity event variable instead of an action variable the main advantage of unity events is you can assign subscribers in the inspector just like you would for button events in my game I've used c-sharp events instead because I prefer to link things up in code in my game I wanted to code a system that allowed me to animate menu screens so I made an animation controller which has a play Enter and play exit code routine then inside these co-routines I looped through all the elements and the animation get the component UI slide in anim and then call play on that script that works fine but what if I want to play a different type of animation well I could use one script that has the logic for every animation but then I'd need a bunch of if statements inside the play function and things could get very messy very quickly depending on how different the animations are it turns out that this problem can be easily solved with an interface interfaces can be used when you want two classes to behave similarly without enforcing a relationship between the classes the structure of an interface is similar to that of a normal class except you don't need a Constructor and you don't actually provide the implementation for any methods when another class implements an interface they are required to provide the implementations in this way an interface represents a contract that guarantees certain methods will exist in a class so how about I make an interface called IUI animation which has the method headers Play Enter and play exit and I'll make the slide-in animation and the fade in animation both implement this interface now we have a way to interface with 2 different scripts in the same way we can simply use git component IUI animation and that will get any script which implements the IUI animation interface and then we just call its play function let's continue on the topic of object oriented programming if you ever have one class that is a certain subcategory of another class you may want to consider using inheritance you can make your class extend another class with a colon the same way you would Implement an interface this means that all public and protected members of the superclass will be accessible by the subclass the main advantage of this is you don't have to rewrite code shared by both classes if for some reason you do want to change the implementation of an inherited method then you can override it in my game I have several types of weapons for now there are assault rifles shotguns and submachine guns which all behave a little differently even though they are different the majority of the code for each weapon is the same so I could either copy and paste the same code it into multiple different strips obviously not a good idea have one script with the code for every weapon which gets messy real fast or I could have multiple gun classes that inherit from a parent class that's the best option in practice this means all the guns can share the same code for generic functionality like ammo fire rate and parsing input oh and what if I want to add melee weapons in the future all weapons need to respond to input and all weapons will have a fire rate so I can put that code into a super super class called weapon now all guns inherit the weapon code from the weapon class as well as the generic gun code from the gun class here's one more trick if I make the weapon class abstract then I don't actually have to implement every method similar to interfaces subclasses will have to provide the implementation of course I can no longer instantiate a weapon by itself because the class is incomplete but that makes sense because I'll never have just a weapon here's a less obvious use case for inheritance that also combines a lot of the previous tips in my game I have Bots and I have players I want each character to use the exact same movement and guns groups just in one instance the input will be provided by the player and in the other instance the input will be calculated by the AI I started off by checking if the player was a bot in each script and then getting the input from the corresponding Source but this got really annoying and messy as I added more mechanics my solution was to make an abstract class called input Handler input Handler has a public method that allows scripts to subscribe to receive input now I have two subclasses of input Handler client input Handler and Bot input Handler these classes send the character input to their subscribers every frame using that character input struct we talked about earlier although they populate the struct in completely different ways because I want to interact with different scripts in the same way I'm using an interface here called I receive input which is implemented by every class who wants to receive input this is a common event listener design pattern here's where the magic happens inside my player scripts all I have to do is get an input Handler component And subscribe to its events since the client and Bot input Handler are both types of input handlers this code will work whether the character has a bot input script or a client's input script attached for the most part all the character scripts don't even need to know if the character is a bot or a real player my weapons have a lot of different stats I need to reference these stats in several different places from the weapon scripts to the weapon selection screen fortunately Unity recently released a feature called scriptable objects which are perfect for bundling data the main advantage they have over normal classes is you can access them in the inspector without having to attach them to a prefab or object in the scene to create a scriptable object just extend Unity scriptable object class and then add the members you want to be included then in the project I can create an instance of that scriptable object by going to new weapon stats now I can rename it and set all the variables here's where things get cool I can replace all the stat variables in my weapon class with a gun stats class and then watch this I can just drag the gun stats object into that field in the inspector and now this gun script will use all the stats from that scriptable object but wait different weapons will use different stacks lucky for me scriptable objects can also extend each other which works exactly as you would expect it to the shotgun stats object now includes all the variables from the gun stats object and because of polymorphism the gun script can use a shotgun stats object the same as it uses a gun stats object I also use a scriptable object to hold the entire weapon set which I use in the weapon selection screen custom editor tools can range from crazy UI menus to simple scripts that interact with the scene outside of play mode even simple tools can massively improve your workflow I just recently started experimenting with these if you saw my map creation video you know that I had to add box colliders throughout the entire scene the problem is Unity generates a navigation mesh with the mesh renderers not the colliders meaning the navigation mesh didn't match the actual colliders this caused my bots to get stuck trying to reach places they couldn't to fix this I made a script that looped through all the colliders and generated a mesh to match the bounds of the Box glider then when I wanted to generate the navmesh I could enable all the meshes to add a button in the inspector follow this template make a normal mono behavior that has the function you want to call then import Unity editor and make a new class that extends editor add the custom editor tag with type of set to the class you just made then override the on inspector GUI function then you can add buttons like I did here I'll close with two super simple tips which also happen to be the most important ones first use a Version Control software I use plastic SCM because that's what Unity provides by default most Version Control software will allow you to push changes to the cloud which means you'll have a backup of your project at every stage in Development I've lost significant portions of my projects before and because I wasn't using Version Control I had to rewrite everything it makes no sense to spend so much time working on something and not go through the extra effort to make physical and online backups as a plus most Version Control software also allows you to easily transfer your project files from computer to computer and easily collaborate with others finally refactor opt-in and write code well the first time many developers including myself write code haphazardly without planning and without preparing for the future usually under the impression that they'll go back and refactor everything at a later date spoiler alert you won't the longer you push it back the more daunting of a task it becomes and the less likely you are to do it even when you are prototyping unless you plan on rebuilding the project from the ground up you should take extra care with how you program and how your systems interact nobody likes to rewrite code that they've already written as a young self-taught game developer I didn't discover these tools and techniques for years hopefully this video helps you to skip the learning curve and expose you to some of the more advanced programming devices that don't get enough attention if anything was completely new to you I encourage you to do some more research on your own in order to completely grasp the concept I know I probably went too fast to fully explain everything I also didn't provide a one-size-fits-all design pattern for you to use in every project because well I don't think that exists now it's your job to implement what you learned and make the best decisions for your own game if you're interested in the game I'm working on check out my devlog series besides that put something you learned in the comments and subscribe for more Game Dev content see you next time
Info
Channel: Tesseract
Views: 55,711
Rating: undefined out of 5
Keywords: unity tutorial, unity, unity3d, spaghetti code, unity coding, clean code, advanced unity tips, advanced unity tutorials, advanced unity programming, advanced unity c#, how to write better code, c# events unity, inheritance unity, singletons unity, game development, indie game development, devlog, game dev, unity tutorial for beginners, unity tutorials, coding, programming, game architecture, beginner unity tutorial, c# tutorial, unity devlog, coding tutorial, better code, c#
Id: dLCLqEkbGEQ
Channel Id: undefined
Length: 22min 23sec (1343 seconds)
Published: Sun Nov 13 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.