20 Advanced Coding Tips For Big Unity Projects

Tesseract
13 Nov 202222:23

Summary

TLDRThis video script offers a wealth of Unity coding tips for advanced developers, aiming to avoid common pitfalls in large-scale game development. It covers best practices like meaningful variable names, commenting, encapsulation, and planning. The script delves into more complex topics such as using enums, co-routines, Singletons, interfaces, inheritance, and scriptable objects for better code structure and maintainability. It also emphasizes the importance of version control and refactoring for scalable game development.

Takeaways

  • πŸ“ **Clear Naming Conventions**: Use concise, meaningful variable names and follow a strict naming convention for consistency, especially in a team environment.
  • πŸ—’οΈ **Frugally Use Comments**: Comments should explain the purpose of complex variables or code blocks, but avoid using them for obvious explanations or as a crutch for old code.
  • πŸ” **Encapsulate with Functions**: Break down long methods and complex logic into smaller, manageable functions to maintain code readability and abstraction.
  • πŸ“˜ **Plan Before Coding**: Sketch out diagrams, flowcharts, and pseudocode before writing the actual code to save time on debugging and rewriting.
  • πŸ” **Use Getters and Setters**: Employ getters and setters to control access to variables, preventing unintended modifications and maintaining encapsulation.
  • πŸ”„ **Component-Based Architecture**: Design scripts as components, allowing for flexibility and independence, which can be easily added or removed without affecting other features.
  • πŸ”‘ **Enums for Fixed Choices**: Utilize enums to represent a set of predefined choices, making it easy to manage and modify options within the Unity Inspector.
  • ⏱️ **Coroutines for Delayed Tasks**: Use coroutines to handle tasks that require delays or sequential execution without blocking the main program flow.
  • πŸ“š **Singleton Pattern**: Implement the Singleton pattern for classes that should have only one instance throughout the application, providing global access while maintaining control.
  • 🎯 **Event-Driven Programming**: Utilize C# events to handle situations where scripts need to react to certain triggers or changes without creating tight coupling between systems.
  • πŸ”„ **Inheritance and Interfaces**: Apply inheritance to share code between related classes and interfaces to ensure that different classes conform to a common structure or behavior.

Q & A

  • What common issue does the video script address in game development?

    -The script addresses the issue of poor coding practices leading to unorganized, unmanageable, and confusing code, often referred to as 'spaghetti code,' which can halt progress and force developers to abandon their projects.

  • What is the first coding tip provided in the script for naming variables?

    -The first coding tip suggests that variable names should be concise, meaningful, and follow a consistent naming convention, such as using Pascal case for public fields, camel case for private instance variables, and all caps for constants.

  • Why is it recommended to use comments in code?

    -Comments are recommended to describe the purpose of complicated variables, explain confusing blocks of code, or remind the developer of things to implement later. However, they should not be used to explain obvious things or to hoard old code.

  • What is the purpose of encapsulating code in functions?

    -Encapsulating code in functions helps maintain a level of abstraction, making it easier to understand what the program is doing at a high level. It also allows for better organization and readability of the code.

  • Why is it advised to plan out code before writing it?

    -Planning out code before writing it can save time spent on debugging and rewriting. It allows developers to think through the logic and structure of the code, leading to more efficient and error-free development.

  • What is the recommended approach for accessing a variable in a separate script?

    -The recommended approach is to make the variable private and then add a public function, known as a getter, which returns the variable's value. This prevents external scripts from modifying the variable directly and causing logic errors.

  • How can the 'SerializeField' attribute be used in Unity?

    -The 'SerializeField' attribute can be used to allow a variable to be accessed in the Unity Inspector without making it publicly accessible from other classes, thus preventing unintended modifications.

  • What is the advantage of using components in Unity's component architecture?

    -Using components allows for a more organized and modular approach to scripting, where each script handles a specific feature or system. This makes it easier to add or remove features without impacting other parts of the game.

  • What is an enum and how can it be used in Unity?

    -An enum is a data type that represents a closed set of choices. In Unity, enums can be set inside the Inspector with a dropdown menu, allowing developers to easily change the value of the enum without directly modifying the code.

  • Why are coroutines useful in game development?

    -Coroutines are useful for spreading a task over multiple frames, allowing for code execution with delays without blocking the main program. They are particularly helpful for methods that contain sequences of events or procedural animations.

  • What is a Singleton pattern and when should it be used?

    -A Singleton pattern is a design pattern that restricts the instantiation of a class to one single instance. It should be used when a script needs to exist within the Unity scene and be referenced by many different scripts, ensuring that only one instance of the script is available at a time.

  • How can interfaces help in object-oriented programming?

    -Interfaces help in object-oriented programming by defining a contract for methods that must be implemented by any class that adopts the interface. This allows for similar behavior across different classes without enforcing a relationship between them.

  • What is the benefit of using inheritance in game development?

    -Inheritance allows a class to extend another class, inheriting its public and protected members. This promotes code reuse and organization, preventing the need to rewrite shared code across different classes.

  • How can scriptable objects improve game development workflow?

    -Scriptable objects allow for bundling data that can be accessed in the Inspector without attaching them to a prefab or object in the scene. They are beneficial for managing game data and assets that need to be editable in the Unity Editor.

  • What is the importance of using version control software in game development?

    -Version control software is crucial for backing up project files at every stage of development, allowing for easy collaboration and the ability to revert to previous versions if needed. It prevents loss of work and streamlines the development process.

  • Why is it important to refactor code and write it well from the start?

    -Refactoring and writing code well from the start is important because it reduces the likelihood of needing to rewrite code later, which can be a daunting and time-consuming task. It also ensures that the code is maintainable and scalable.

Outlines

00:00

😱 Overcoming Spaghetti Code in Game Development

The paragraph discusses the common issue of code becoming unmanageable in large-scale game projects, particularly when dealing with complex systems like a fantasy MMORPG with blockchain features. The speaker emphasizes the importance of scalable and clean coding practices, offering a list of valuable Unity coding tips to avoid project failure. These tips include meaningful variable names, consistent naming conventions, and strategic commenting to clarify code purpose without redundancy. The speaker also introduces the concept of using documentation comments for public methods, advocating for a well-structured approach to coding from the start.

05:02

πŸ“˜ Advanced Unity Coding Practices for Scalability

This section delves into advanced Unity coding techniques, starting with the importance of encapsulating code in functions to maintain readability and abstraction. The speaker shares personal experiences, such as moving from a monolithic update function to a more modular approach. Planning code with diagrams and pseudocode before implementation is recommended to save time on debugging. The paragraph also covers the use of private variables with public getters for controlled access, the use of SerializeField to expose variables in the inspector safely, and the component-based architecture for writing modular scripts that can be managed independently.

10:02

πŸ”„ Leveraging Coroutines and Structs for Efficient Coding

The speaker introduces coroutines as a powerful tool for managing tasks over multiple frames without blocking the main program execution, illustrating their use with examples like match start sequences and dynamic footsteps. Structs are highlighted as lightweight data containers that are copied upon assignment, making them ideal for immutable data types. The paragraph also touches on the use of enums for predefined choices, such as weapon classes or game states, and how they can be set via the inspector for convenience.

15:04

🎯 Implementing Design Patterns for Clean Architecture

This part of the script focuses on design patterns such as the Singleton for managing unique instances like game managers, and the component architecture for creating reusable and interchangeable scripts. The use of interfaces for shared behavior without enforcing a class relationship is discussed, along with inheritance to avoid code duplication and to create a hierarchy of classes. The speaker also explains how to use scriptable objects for bundling data accessible in the inspector and how to employ custom editor tools to improve the workflow.

20:05

πŸ› οΈ Refactoring and Best Practices for Game Development

The final paragraph stresses the importance of refactoring and writing clean code from the outset. The speaker shares personal anecdotes about the pitfalls of postponing refactoring and encourages developers to adopt best practices even during prototyping. The benefits of using version control software for project backups, collaboration, and file management are highlighted. The paragraph concludes with a call to action for developers to apply the learned techniques and make informed decisions for their game projects, while also inviting viewers to follow the speaker's devlog series for more insights.

Mindmap

Keywords

πŸ’‘Spaghetti code

Spaghetti code refers to a disorganized and complex code structure that is difficult to understand and maintain. In the video, it is used to describe the chaotic state of the project when code becomes excessively long and interdependent, leading to a confusing and unmanageable development process.

πŸ’‘Variable naming conventions

Variable naming conventions are the rules used to name variables in a way that is clear, consistent, and meaningful. The video emphasizes the importance of using concise but meaningful variable names and function names as verbs to improve code readability and maintainability, with examples of specific conventions like Pascal case for public fields and camel case for private variables.

πŸ’‘Comments

Comments are annotations in the code that explain the purpose or functionality of the code, without affecting its execution. The video advises using comments sparingly but effectively, to describe complex variables, explain confusing code blocks, or remind of future implementations, rather than explaining obvious aspects or hoarding old code.

πŸ’‘Encapsulation

Encapsulation is a principle in object-oriented programming that involves bundling related data and methods within a single unit or class. The video suggests using encapsulation to improve code readability by breaking down long methods and complex logic into smaller, more manageable functions.

πŸ’‘Singleton

A Singleton is a design pattern that restricts the instantiation of a class to one single object, ensuring a consistent point of access. The video discusses using Singletons for manager scripts like game manager or network manager, which need to be referenced by multiple scripts throughout the game.

πŸ’‘Component architecture

Component architecture is a design approach where scripts are designed to function as independent components, each handling a specific feature or system. The video illustrates this concept by recommending the creation of separate scripts for different functionalities of a player object, such as movement, health, and weapon handling.

πŸ’‘Enums

Enums, or enumerations, are a way to define a set of named constants that represent a closed set of values. In the video, enums are used to represent weapon classes and the current state of the match, and they can be set via a dropdown menu in the Unity inspector, providing a user-friendly way to manage predefined options.

πŸ’‘Coroutines

Coroutines are a feature in Unity that allows tasks to be spread over multiple frames, enabling the execution of code with delays without blocking the main program. The video explains how coroutines are useful for running sequences of events or procedural animations and provides examples of their use in the game's match start sequence and character footsteps.

πŸ’‘Inheritance

Inheritance is an object-oriented programming concept where a class (subclass) can inherit properties and methods from another class (superclass). The video discusses using inheritance to avoid code duplication and to create a hierarchy of classes that share common functionality, such as different types of weapons in the game.

πŸ’‘Scriptable Objects

Scriptable Objects in Unity are a way to create data-oriented classes that can be edited in the inspector and used across different parts of the game. The video describes using Scriptable Objects to bundle weapon stats and other data, making it easy to manage and modify game data without modifying the code.

πŸ’‘Interfaces

Interfaces define a contract with a set of methods that must be implemented by any class that uses the interface. The video explains how interfaces can be used to create a common way to interact with different scripts, such as creating an IUIAnimation interface for different types of UI animations.

πŸ’‘Version Control

Version Control is a system that records changes to a project's files over time, allowing developers to manage and keep track of their work. The video emphasizes the importance of using version control software to back up projects, collaborate with others, and manage changes efficiently.

πŸ’‘Refactoring

Refactoring is the process of restructuring existing code to improve its readability, performance, or maintainability without changing its external behavior. The video suggests that developers should refactor and write code well from the start, rather than postponing it for later, to avoid the daunting task of rewriting large portions of the code.

Highlights

Starting work on a dream game: a fantasy MMORPG sandbox Battle Royale with a blockchain economy.

Challenges faced with unorganized and spaghetti code leading to project scrapping.

Importance of variable names being concise, meaningful, and following a naming convention.

Use of comments to describe the purpose of complicated variables and blocks of code.

Encapsulating code in functions to maintain abstraction and simplify debugging.

Planning code before writing to save time on debugging and rewriting.

Using private variables with public getters for controlled access and logic error prevention.

Utilizing SerializeField to expose variables in the inspector without external class access.

Component-based architecture for scripts to enhance modularity and reusability.

Enums for representing a closed set of choices and setting values in the inspector.

Coroutines for spreading tasks over multiple frames without blocking the main execution.

Singleton pattern for creating a class with only one instance for global access.

Manager classes for handling shared information across multiple scripts.

C# events for event-driven programming to reduce dependency between systems.

Using interfaces for shared behavior without enforcing a relationship between classes.

Inheritance for code reuse and maintaining a relationship between classes.

Scriptable Objects for bundling data and accessing them in the inspector.

Custom editor tools for improving workflow and scene interaction.

The necessity of using version control software for project backup and collaboration.

The importance of refactoring and writing clean code from the start.

Transcripts

play00:00

so you finally decided to Begin work on

play00:02

your dream game a fantasy MMORPG sandbox

play00:06

Battle Royale powered by a blockchain

play00:08

economy what could possibly go wrong

play00:10

then two months later progress comes to

play00:14

a grinding halt you have scripts that

play00:16

are a thousand lines long you've

play00:17

forgotten what your old code does adding

play00:20

new features means you have to rewrite

play00:21

three old ones every script relies on

play00:24

every other one and overall your project

play00:26

becomes an unorganized unmanageable

play00:29

confusing dumpster fire of spaghetti

play00:31

code tragically you are forced to scrap

play00:34

the project and give up on your Game Dev

play00:36

dreams sound familiar there are hundreds

play00:39

of hours of unity tutorials online but

play00:42

very few are geared towards more

play00:44

advanced developers aiming to create

play00:45

large-scale commercial games that's why

play00:48

I've compiled a list of some of the most

play00:50

valuable Unity coding tips that I've

play00:52

learned over the years along with

play00:54

examples of how I've actually used these

play00:56

techniques in my own game hopefully by

play00:58

the end of this video you'll have the

play00:59

tool tools you need to write scalable

play01:01

well-structured clean code that won't

play01:04

come back to bite you down the road I've

play01:05

ordered these roughly by difficulty so

play01:08

stay till the end for the most advanced

play01:09

techniques let's start off with the

play01:12

simple one variable names many new

play01:14

programmers get way too excited when

play01:16

they realize that they can name

play01:18

variables whatever they want in case

play01:20

this is you consider that when you come

play01:22

back to a script after a few weeks

play01:23

there's no way you're going to remember

play01:25

what e and sussie and your mom mean

play01:29

variable names should be concise but

play01:31

meaningful and function names should be

play01:33

verbs if you really want to go above and

play01:36

beyond consider following some sort of

play01:38

strict naming convention

play01:39

I decided to use the standards outlined

play01:42

in the c-sharp documentation public

play01:44

fields are Pascal case private instance

play01:47

variables start with an underscore and

play01:49

our camel case and constants are in all

play01:51

caps you can also Define your own rules

play01:54

and conventions if they make sense in

play01:55

your project for example I used camel

play01:58

case for variables exposed to the unity

play02:00

editor and since I'm making a

play02:02

multiplayer game I've decided to start

play02:04

all networked variables with the prefix

play02:06

net the key here is consistency

play02:09

especially if you're working with a team

play02:11

moving on to equally groundbreaking

play02:13

advice comments use them frugally but

play02:17

definitely use them

play02:19

use them to describe the purpose of

play02:21

complicated instance variables explain

play02:23

confusing blocks of code or remind

play02:25

yourself of things to implement later do

play02:27

not use them to explain things that are

play02:29

obvious and do not use them to hoard old

play02:31

code for weeks on end that's what

play02:33

version control is for comments are

play02:36

especially useful for explaining the

play02:37

purpose of functions this is something I

play02:39

picked up from my University programming

play02:41

class the professor insists that every

play02:43

function should have a comment

play02:44

summarizing that method on public

play02:47

methods consider using documentation

play02:49

comments which you can use to provide

play02:51

pre-slash post conditions a description

play02:53

of parameters and an explanation of the

play02:56

return value

play02:57

continuing with code readability

play03:00

encapsulate code in functions wherever

play03:02

possible instead of having methods that

play03:04

are 100 lines long and if statements

play03:07

nested five times break up your logic

play03:09

into functions and break up those

play03:11

functions into more functions this helps

play03:13

to maintain a level of abstraction so

play03:16

that anyone can tell what your program

play03:17

is doing at the highest level then if

play03:20

you want to get into the weeds you can

play03:21

look at the implementation of one

play03:23

function at a time I used to pile all my

play03:26

logic into the update function and I

play03:28

would put a comment above each block of

play03:30

code explaining what it did if you use

play03:32

well-named functions you shouldn't need

play03:34

comments to explain what your code is

play03:36

doing

play03:37

plan out your code before you write it

play03:39

you can do this with a good

play03:40

old-fashioned paper and pen in a Word

play03:43

document or even directly in your IDE

play03:45

with comments I'm talking diagrams flow

play03:49

charts pseudo code whatever floats your

play03:51

boat trust me the time saved from

play03:53

debugging and rewriting code will be

play03:55

worth a little extra forethought in the

play03:57

end

play03:58

sometimes you'll need access to a

play04:00

variable in a separate script you may be

play04:03

used to Simply making this variable

play04:04

public but this means that the value can

play04:07

be changed by any script in your project

play04:09

leaving this variable open to external

play04:11

modification can cause logic errors and

play04:13

cause the class to behave in unexpected

play04:15

ways for example there's a reason why

play04:17

c-sharp doesn't let us modify the count

play04:19

variable of a list the standard solution

play04:21

is to make the variable private and then

play04:23

add a public function which Returns the

play04:25

variable's value called a getter c-sharp

play04:28

has a shorthand way to write these

play04:30

Getters and Setters if you want a

play04:32

variable to be publicly accessible but

play04:34

not publicly alterable make the setter

play04:36

private and the getter public

play04:38

a similar problem arises when we want to

play04:41

be able to set a variable from the

play04:43

inspector most tutorials will tell you

play04:45

to just make this variable public as we

play04:48

just discussed making a variable public

play04:50

can cause other issues luckily you can

play04:52

use a tag called serialize field instead

play04:55

this will allow you to access the

play04:57

variable in the inspector but not from

play04:59

other classes it will also clean up your

play05:01

intellisense Unity is built around a

play05:04

component architecture so it makes sense

play05:06

to write our code like components as

play05:08

well this means instead of having one

play05:10

script called player which handles

play05:12

everything having to do with the player

play05:13

object you should make a new script for

play05:15

each new feature system for example in

play05:18

my game I have six scripts on my player

play05:20

object a separate one for input movement

play05:23

Health weapon handling grenade throwing

play05:26

and UI each script works almost entirely

play05:28

separately from the others ideally you

play05:30

should be able to add and remove these

play05:32

components freely without impacting the

play05:34

other features if you absolutely need to

play05:37

reference other systems you can use the

play05:39

require component tag to ensure that

play05:42

that script will always exist on the

play05:44

object additionally it's best to design

play05:46

these components so that they work as

play05:47

intended on any game object for example

play05:49

I wrote my health class in a way that

play05:51

allows me to add it to any game object

play05:53

that has help such as barrels or drones

play05:56

in the future this is one of the main

play05:58

advantages of the component architecture

play06:00

enums can be used to represent a closed

play06:03

set of choices for example I use them to

play06:05

represent the weapon classes in my game

play06:07

and also to represent the current state

play06:10

of the match the coolest part is you can

play06:12

set the value of an enum inside the

play06:14

inspector with a drop down menu in order

play06:17

to use an enum declare the possible

play06:19

values in a list like this then you can

play06:21

make variables of that type which store

play06:23

one of the choices you specified

play06:25

co-routines always seemed very scary to

play06:27

me but now that I know how they work I

play06:30

found them to be super useful basically

play06:32

co-routines spread a task over multiple

play06:34

frames allowing you to run code with

play06:37

delays without blocking the main

play06:38

execution of your program they are most

play06:41

useful for methods that contain a

play06:43

sequence of events or a procedural

play06:45

animation please note Co routines still

play06:47

run on the main thread so using them is

play06:50

not the same as threading I use a CO

play06:52

routine to play my match start sequence

play06:54

since it involves a lot of sequential

play06:56

time delays I also use a co-routine to

play06:58

repeatedly Place footsteps underneath my

play07:00

characters to declare a co-routine

play07:02

create a function with a return type I

play07:05

enumerator then inside the function you

play07:07

can use yield return to pause the

play07:09

execution and continue in the same spot

play07:12

later on yield return new weight for

play07:15

seconds delays for a certain amount of

play07:16

time and yield return new wait until

play07:19

delays until the specified condition is

play07:22

true this is a great way to avoid a lot

play07:24

of messy timer with time.deltatime you

play07:27

can start a co-outine by calling start

play07:29

Co routine and passing in the function

play07:31

name as a string if you want to call a

play07:33

function after a certain one-time time

play07:35

delay then it may be easier to use the

play07:37

invoke method basically you can call

play07:39

this and pass in your function name as a

play07:41

string parameter along with a time in

play07:43

seconds this will call your method after

play07:46

the specified delay you can also use

play07:48

invoke repeating which will call a

play07:50

method repeatedly at certain time

play07:51

intervals I don't need the bots in my

play07:54

game to calculate a new destination

play07:56

every frame so I use invoke repeating to

play07:59

update their path every half a second

play08:00

instead structs are kind of like a light

play08:03

version of classes a key difference

play08:06

between structs and classes is that a

play08:08

struct is a value type while classes are

play08:10

reference types in this way structs

play08:12

behave similar to Primitives every time

play08:15

you assign a struct to a variable that

play08:17

struct is copied you should use a struct

play08:19

to store immutable data types that

play08:21

logically represent only a single value

play08:24

a lot of the time I use a struct when I

play08:26

want to have a list of some custom data

play08:28

types show up in the inspector for

play08:30

example I have a struct defined in my

play08:32

player UI manager which represents a UI

play08:35

group if I Mark the struct as

play08:37

serializable then I can set the fields

play08:39

of each UI group in the inspector I also

play08:42

use a structure to store and transmit

play08:44

input for the current frame since only

play08:46

the input Handler class should be able

play08:48

to change the input

play08:50

now we're getting to the good stuff a

play08:52

Singleton is a type of class which only

play08:54

allows one instance of itself to ever be

play08:57

created

play08:58

to implement a Singleton make a static

play09:00

variable of the same type as the class

play09:02

then in the awake method store the new

play09:05

instance into the static variable or

play09:07

destroy the script if the instance has

play09:09

already been assigned if you don't know

play09:10

what static variables are there are

play09:12

variables that belong to the whole class

play09:14

instead of just one object this means

play09:17

that you can access static members from

play09:19

anywhere with just the class name hey

play09:21

that's pretty convenient the issue is

play09:24

non-static members cannot be used in

play09:26

static functions a Singleton gives us

play09:28

the best of both worlds if you use the

play09:31

static instance variable to reference

play09:33

the actual instance of the script in the

play09:35

scene then you can access all the

play09:37

non-static members as if they were

play09:39

static Singletons are most suited for

play09:41

scripts that need to exist within the

play09:43

unity scene but also need to be

play09:45

referenced by many different scripts and

play09:47

remember there can only be one instance

play09:49

of the script in the scene at a time so

play09:51

don't try to use a Singleton for your

play09:53

enemy script since there will likely be

play09:55

many enemies in your scene at one time I

play09:57

use Singletons for my manager scripts

play09:59

like my game manager and network manager

play10:02

I also use it for utilities like my

play10:04

object pool the object pool needs to be

play10:06

in the scene but I also want to be able

play10:08

to reference it from anywhere as part of

play10:11

this tip I'm going to suggest that you

play10:12

create a manager class anytime you have

play10:14

a bunch of different scripts that need

play10:16

to access a set of information for

play10:18

example the current game State and the

play10:20

list of players on each team is

play10:21

important for many different scripts so

play10:23

that's why I created a Singleton game

play10:25

manager class to handle these systems

play10:28

and expose the information to anyone who

play10:30

needs it just remember that these

play10:31

managers should still only have one job

play10:34

each and should not be trying to handle

play10:35

too much at once

play10:37

in-game development much of our code is

play10:40

event driven this means most of the time

play10:42

we're just sitting around waiting for

play10:44

something to happen c-sharp events are

play10:46

well suited for this type of programming

play10:47

and they can help remove dependency

play10:49

between systems in my game the player

play10:51

movement script and the grenade script

play10:53

need to know when the match begins in

play10:55

order to reset their respective action

play10:57

cooldowns but the game manager handles

play11:00

the start of the match

play11:01

how should I solve this problem

play11:03

well I could Loop through every player

play11:06

in the match and then get a reference to

play11:07

the player movement and throw grenade

play11:09

script and then call a public function

play11:11

reset cooldown on each this would work

play11:14

but it makes my manager script dependent

play11:16

on the player scripts meaning I can't

play11:18

change my player scripts without

play11:20

changing the manager script let's use

play11:22

c-sharp events to solve this problem

play11:24

first I'll make a variable of type

play11:26

action inside the game manager called

play11:28

game started to trigger this event I'll

play11:31

call Dot invoke on that action variable

play11:33

I use a question mark before the dot

play11:35

operator to avoid null reference

play11:37

exceptions you can also add parameters

play11:39

like this but I don't need parameters in

play11:42

this case now my player script can

play11:44

subscribe to this event by referencing

play11:46

the action variable and then using plus

play11:48

equals to link its own function to that

play11:51

event now when the game started event is

play11:53

invoked by the game manager all the

play11:55

scripts which I've hooked into this

play11:56

action will be notified this means that

play11:59

the game manager does not have to deal

play12:00

with my player scripts at all and best

play12:03

of all if another script needs to

play12:04

reference this event in the future then

play12:06

I won't have to change my game manager

play12:08

to include it in general it's okay if

play12:10

your scripts depend on your manager

play12:12

classes but not the other way around I

play12:14

also use c-sharp events for my health

play12:16

quests I have an event for when the

play12:18

health is changed and for when the

play12:20

object has died this allows me to

play12:22

separate the health and regen logic from

play12:24

stuff like UI and death logic which is

play12:27

handled in a different script now I can

play12:30

use Health as a component that can be

play12:32

reused and added to any object Unity has

play12:35

its own event system which you may

play12:37

prefer it works in almost the same way

play12:39

as a c-sharp event except you define a

play12:42

Unity event variable instead of an

play12:44

action variable the main advantage of

play12:46

unity events is you can assign

play12:48

subscribers in the inspector just like

play12:50

you would for button events in my game

play12:52

I've used c-sharp events instead because

play12:54

I prefer to link things up in code

play12:57

in my game I wanted to code a system

play12:59

that allowed me to animate menu screens

play13:01

so I made an animation controller which

play13:04

has a play Enter and play exit code

play13:07

routine then inside these co-routines I

play13:09

looped through all the elements and the

play13:10

animation get the component UI slide in

play13:14

anim and then call play on that script

play13:16

that works fine but what if I want to

play13:19

play a different type of animation

play13:21

well I could use one script that has the

play13:24

logic for every animation but then I'd

play13:26

need a bunch of if statements inside the

play13:28

play function and things could get very

play13:31

messy very quickly depending on how

play13:33

different the animations are it turns

play13:35

out that this problem can be easily

play13:36

solved with an interface interfaces can

play13:39

be used when you want two classes to

play13:41

behave similarly without enforcing a

play13:44

relationship between the classes the

play13:46

structure of an interface is similar to

play13:48

that of a normal class except you don't

play13:50

need a Constructor and you don't

play13:52

actually provide the implementation for

play13:54

any methods when another class

play13:55

implements an interface they are

play13:57

required to provide the implementations

play13:59

in this way an interface represents a

play14:02

contract that guarantees certain methods

play14:04

will exist in a class so how about I

play14:07

make an interface called IUI animation

play14:09

which has the method headers Play Enter

play14:12

and play exit and I'll make the slide-in

play14:14

animation and the fade in animation both

play14:17

implement this interface now we have a

play14:19

way to interface with 2 different

play14:22

scripts in the same way we can simply

play14:24

use git component IUI animation and that

play14:27

will get any script which implements the

play14:29

IUI animation interface and then we just

play14:32

call its play function let's continue on

play14:35

the topic of object oriented programming

play14:37

if you ever have one class that is a

play14:39

certain subcategory of another class you

play14:42

may want to consider using inheritance

play14:43

you can make your class extend another

play14:45

class with a colon the same way you

play14:48

would Implement an interface this means

play14:50

that all public and protected members of

play14:52

the superclass will be accessible by the

play14:54

subclass the main advantage of this is

play14:57

you don't have to rewrite code shared by

play14:59

both classes if for some reason you do

play15:01

want to change the implementation of an

play15:03

inherited method then you can override

play15:06

it in my game I have several types of

play15:08

weapons for now there are assault rifles

play15:11

shotguns and submachine guns which all

play15:13

behave a little differently even though

play15:15

they are different the majority of the

play15:17

code for each weapon is the same so I

play15:19

could either copy and paste the same

play15:21

code it into multiple different strips

play15:23

obviously not a good idea have one

play15:25

script with the code for every weapon

play15:27

which gets messy real fast or I could

play15:30

have multiple gun classes that inherit

play15:32

from a parent class that's the best

play15:34

option in practice this means all the

play15:37

guns can share the same code for generic

play15:39

functionality like ammo fire rate and

play15:41

parsing input oh and what if I want to

play15:44

add melee weapons in the future all

play15:47

weapons need to respond to input and all

play15:49

weapons will have a fire rate so I can

play15:51

put that code into a super super class

play15:54

called weapon now all guns inherit the

play15:57

weapon code from the weapon class as

play15:59

well as the generic gun code from the

play16:01

gun class here's one more trick if I

play16:03

make the weapon class abstract then I

play16:06

don't actually have to implement every

play16:07

method similar to interfaces subclasses

play16:10

will have to provide the implementation

play16:12

of course I can no longer instantiate a

play16:15

weapon by itself because the class is

play16:16

incomplete but that makes sense because

play16:19

I'll never have just a weapon here's a

play16:21

less obvious use case for inheritance

play16:23

that also combines a lot of the previous

play16:25

tips in my game I have Bots and I have

play16:28

players I want each character to use the

play16:30

exact same movement and guns groups just

play16:33

in one instance the input will be

play16:35

provided by the player and in the other

play16:37

instance the input will be calculated by

play16:39

the AI I started off by checking if the

play16:41

player was a bot in each script and then

play16:43

getting the input from the corresponding

play16:45

Source but this got really annoying and

play16:48

messy as I added more mechanics my

play16:50

solution was to make an abstract class

play16:52

called input Handler input Handler has a

play16:55

public method that allows scripts to

play16:57

subscribe to receive input now I have

play16:59

two subclasses of input Handler client

play17:02

input Handler and Bot input Handler

play17:04

these classes send the character input

play17:07

to their subscribers every frame using

play17:09

that character input struct we talked

play17:11

about earlier although they populate the

play17:13

struct in completely different ways

play17:15

because I want to interact with

play17:16

different scripts in the same way I'm

play17:18

using an interface here called I receive

play17:20

input which is implemented by every

play17:23

class who wants to receive input this is

play17:25

a common event listener design pattern

play17:27

here's where the magic happens inside my

play17:30

player scripts all I have to do is get

play17:32

an input Handler component And subscribe

play17:34

to its events since the client and Bot

play17:37

input Handler are both types of input

play17:39

handlers this code will work whether the

play17:41

character has a bot input script or a

play17:43

client's input script attached for the

play17:45

most part all the character scripts

play17:47

don't even need to know if the character

play17:49

is a bot or a real player my weapons

play17:51

have a lot of different stats I need to

play17:54

reference these stats in several

play17:55

different places from the weapon scripts

play17:57

to the weapon selection screen

play17:59

fortunately Unity recently released a

play18:01

feature called scriptable objects which

play18:03

are perfect for bundling data the main

play18:05

advantage they have over normal classes

play18:07

is you can access them in the inspector

play18:09

without having to attach them to a

play18:11

prefab or object in the scene

play18:14

to create a scriptable object just

play18:16

extend Unity scriptable object class and

play18:19

then add the members you want to be

play18:20

included then in the project I can

play18:23

create an instance of that scriptable

play18:24

object by going to new weapon stats now

play18:28

I can rename it and set all the

play18:30

variables here's where things get cool I

play18:32

can replace all the stat variables in my

play18:35

weapon class with a gun stats class and

play18:38

then watch this I can just drag the gun

play18:40

stats object into that field in the

play18:43

inspector and now this gun script will

play18:45

use all the stats from that scriptable

play18:47

object but wait different weapons will

play18:49

use different stacks

play18:50

lucky for me scriptable objects can also

play18:53

extend each other which works exactly as

play18:55

you would expect it to the shotgun stats

play18:58

object now includes all the variables

play18:59

from the gun stats object and because of

play19:02

polymorphism the gun script can use a

play19:04

shotgun stats object the same as it uses

play19:07

a gun stats object

play19:09

I also use a scriptable object to hold

play19:11

the entire weapon set which I use in the

play19:13

weapon selection screen custom editor

play19:16

tools can range from crazy UI menus to

play19:19

simple scripts that interact with the

play19:21

scene outside of play mode even simple

play19:23

tools can massively improve your

play19:25

workflow I just recently started

play19:27

experimenting with these if you saw my

play19:29

map creation video you know that I had

play19:31

to add box colliders throughout the

play19:33

entire scene the problem is Unity

play19:36

generates a navigation mesh with the

play19:38

mesh renderers not the colliders meaning

play19:41

the navigation mesh didn't match the

play19:43

actual colliders this caused my bots to

play19:45

get stuck trying to reach places they

play19:47

couldn't to fix this I made a script

play19:49

that looped through all the colliders

play19:51

and generated a mesh to match the bounds

play19:53

of the Box glider then when I wanted to

play19:56

generate the navmesh I could enable all

play19:58

the meshes to add a button in the

play20:00

inspector follow this template make a

play20:02

normal mono behavior that has the

play20:04

function you want to call then import

play20:06

Unity editor and make a new class that

play20:09

extends editor add the custom editor tag

play20:12

with type of set to the class you just

play20:14

made then override the on inspector GUI

play20:17

function then you can add buttons like I

play20:19

did here

play20:20

I'll close with two super simple tips

play20:23

which also happen to be the most

play20:24

important ones first use a Version

play20:27

Control software I use plastic SCM

play20:30

because that's what Unity provides by

play20:32

default most Version Control software

play20:35

will allow you to push changes to the

play20:37

cloud which means you'll have a backup

play20:38

of your project at every stage in

play20:40

Development I've lost significant

play20:42

portions of my projects before and

play20:45

because I wasn't using Version Control I

play20:47

had to rewrite everything it makes no

play20:49

sense to spend so much time working on

play20:51

something and not go through the extra

play20:53

effort to make physical and online

play20:54

backups as a plus most Version Control

play20:57

software also allows you to easily

play20:59

transfer your project files from

play21:01

computer to computer and easily

play21:03

collaborate with others

play21:05

finally refactor opt-in and write code

play21:08

well the first time many developers

play21:10

including myself write code haphazardly

play21:12

without planning and without preparing

play21:14

for the future usually under the

play21:16

impression that they'll go back and

play21:18

refactor everything at a later date

play21:19

spoiler alert you won't the longer you

play21:22

push it back the more daunting of a task

play21:24

it becomes and the less likely you are

play21:26

to do it even when you are prototyping

play21:28

unless you plan on rebuilding the

play21:30

project from the ground up you should

play21:31

take extra care with how you program and

play21:33

how your systems interact nobody likes

play21:36

to rewrite code that they've already

play21:37

written

play21:38

as a young self-taught game developer I

play21:41

didn't discover these tools and

play21:42

techniques for years hopefully this

play21:44

video helps you to skip the learning

play21:46

curve and expose you to some of the more

play21:48

advanced programming devices that don't

play21:50

get enough attention if anything was

play21:52

completely new to you I encourage you to

play21:54

do some more research on your own in

play21:56

order to completely grasp the concept I

play21:58

know I probably went too fast to fully

play22:00

explain everything I also didn't provide

play22:03

a one-size-fits-all design pattern for

play22:05

you to use in every project because well

play22:07

I don't think that exists now it's your

play22:09

job to implement what you learned and

play22:11

make the best decisions for your own

play22:12

game if you're interested in the game

play22:14

I'm working on check out my devlog

play22:16

series besides that put something you

play22:18

learned in the comments and subscribe

play22:20

for more Game Dev content see you next

play22:22

time

Rate This
β˜…
β˜…
β˜…
β˜…
β˜…

5.0 / 5 (0 votes)

Related Tags
Unity CodingGame DevelopmentScript OptimizationCode ManagementBest PracticesSoftware ArchitectureVersion ControlSingleton PatternEvent-Driven ProgrammingOOP PrinciplesGame Design