Posted August 13, 2019 by Arc
#unity #Tutorial #C# #Coding #architecture #GMTK2019 #OnlyOne #GMTK #Interface #Unity3D #Unity #Properties #OpenSource
Archi-lecture
you can skip this to the actual stuff under next headline
I just participated in GMTK 2019 game jam and actually made a game in 2 Days. It was my first jam and was so much fun competing against the clock with a team. I found out how easy it is to finish a game when you limit yourself, in this case, theme and timings.
Theme was "Only One" which I found out is very common jam theme from friends, but quite new for me at least. for me It was past midnight when the theme was announced and I started brain storming right away. What could "Only one" be. I started telling a non game dev friend about it as I find it easier to think when i am typing about the thing. we talked for a while about ideas and things that would work for theme. "One bullet per gun?" thats what i thought for the night and went to bed, thinking about architecture and fell asleep after 3 like usual, after coming up with "One shot left" as the title.
woke up after 11:30 and got to work in few hours, when talking to another friend who was participating I realized how common is "One bullet" idea and I wouldn't be making anything original at all. Then I knew I need something else too and i was planning to add 3 types of enemies anyways so I Thought why not lets add elemental damage and each enemy only dies to one specific element. I knew what to do after that! and I renamed it to "One Last Shot"
And I had no art for now, although I did asked few artists to help in the jam if they can, but it was night at where they were so i just worked with basic shapes till then.(I was even ready to submit my game with no art if i had to, but artists didn't disappoint)
I know No one is interested in how I came up with the idea so Lets Talk about the coding architecture that I made for the project, So only people interested in programming read further as things are going to get sharp. c sharp.. nvm
Arc-itecture
It was just a jam, I could just go with spaghetti coding but I wanted to practice some complex(not really) architecture and use this project as a test for what I learned in last 1 year of game development. Three major things we are going discuss is "Interface, Properties and Events" but I am still not gonna go in detail as this post is already too big, and i haven't even started yet.
So I started by making some flow charts for the architecture and First one shows how It could be done for a jam, where All the functions of player could be simply added in "only one" Script, here its called "PlayerController".
but there are some disadvantages about it:
What could be done is this:
Here you have one controller that other scripts can rely on for the player input. Like movement script would ask player controller on what buttons are pressed, so it can do what it does, move the player.
Pros of this:
- Now you can easily take down one script and player would still work.
- You can Add another class instead of editing your old code. making everything cleaner.
- Easy to debug as you would know what class must be causing problem.
but.. its still not reusable as all other classes depends upon player controller script so all they can do is control player.
Now lets talk about interfaces.
If we Add an interface for each functionality class(Movement, weapon controller etc) to rely on, instead of implementing them, We can do this.
Now all Scripts depends on their own respective Interface, instead of one class, and how would that be helpful you ask?
Lets say now you want a movement script for your enemies, and you already have one, so All you need to do is create an "AiController" class which would implement "IMovementInput" as well and write logic in it so movement class would be controlled by your AI logic in "AiController".
Reusability achieved!
All the other pros of previous architecture are also here. Interfaces are very important and should be considered over direct references for loose coupling.
Dash script also depends upon movement because i disable player to change direction while dashing. we could do that in other ways too, like checking whether player is dashing in player controller script. but i just got lazy.. i didn't have much time remember? I will fix it later. procasti-later
What about properties?
I used to think they were quite useless but turned out they are amazing!
They don't just limit the accessibility of a variable but can also be used for checks and/or some logic to run when their value is changed. very handy.
other thing about them is, you can add properties in an interface unlike variables, which are not allowed.
That's how IWeaponInput looks like. it has 2 float properties for shooting direction and a bool for when to shoot and a function called "SetInputs()" that other scripts call to get updated values of direction and isShooting, through the interface.
Properties and Functions in interface need to be implemented in every class that is implementing from the the interface, that class in our case is playerController.
It implements all the different interface and Other scripts don't care about playerControllers. what they need is their respective interface and the values within that interface. they easily use "GetComponent<IWeaponInput>()" to get the interface and cache them in variable of type of the interface, IWeaponInput in this example.
keeping the set of properties private ensures that only this class can change the values, which helps us to avoid accidentally changing these from other classes, which is reasonable in at least this case.
Where are the Events?
Unity has event system that you see around in some components like UI Buttons where you can add functions to be called onClick through the inspector. which is very handy and you can add them to your own scripts as well quite easily, just made them [SerializedField] and you can access them from inspector.
This is my final Player with all the scripts ready. It could still be improved a lot but this is how it looks for now.
Player Inspector
I added UnityEvent on Health behavior because not every character does the same thing on death. if player dies, its a game over when an enemy dies, we count the deaths. You can see how handy event system is.
Here is the enemy inspector. using AIFollowController instead of PlayerController and same character movement script with an updated health script which inherits from same old health script, but can only take elemental damages.
Pro-Tip: You can use scriptable objects to create an event system and easily invoke it using UnityEvent. this way your code doesn't depend on anything in current scene, so easily usable among different scenes.
Well I think the post is too long now, so I will just end it already.
All the code is available on my github as well.
If you want to see code in action play the game here
Thanks for reading, and sorry if its not clear to understand.. I tried. if you have any questions, you can ask me in comments or dm. ^^