I have to say I’m a little surprised at the lack of a re-usable game framework for Unity.
I’m talking about something above the built-in API that helps divide scripts and GameObjects in the scene based on what design purpose they’re intended to follow (the best example that comes to mind is Ruby on Rails). I’m no stranger to game development; I just hate the quirks of a pure Actor-Update model and believe a forced one-way dependancy system is a much cleaner and quicker way to get going on a new project without having to unnecessarily refactor down the line.
Nicholas Francis, Jonathan Czeck, Matthew Wegner, and everyone else who has published multiple highly successful Unity games: What kind of framework do you use to get games built quickly and efficiently?
We organize scripts for a project into two big categories–all of the game-specific stuff, and then a Library folder that we carry from project to project. Our script library contains everything from single-line scripts (RigidbodySleepOnAwake) to complex systems (our ScoreKeeper).
At the end of a project we take a look at new systems created for the game and decide which, if any, we could graduate to “Library” status by generalizing them. We allow Library scripts to freely depend on each other.
Some of our systems are Blurst-specific, but they’re all quite drop-in. Adding an achievement to a game is a single line of code: BlurstAchievements.use.Did(101). We have a lot of low-level gameplay systems we reuse: A ScoreKeeper that manages complex combo multipliers/timers, a RecordKeeper that easily tracks game numbers across sessions through Blurst profiles, a SoundPlayer that allows easy setup of multiple sounds for one events, etc.
This is the Crane Wars hieararchy–all of the items with a double-space prefix are global prefabs that exist across all Unity levels. These are all library items.
Finally, our projects share a series of levels as the game shell (branding, login, main menu, gameplay). Our Global library script deals with starting a game, returning to the title, etc.
We could make a new game where you press the 1-5 keys to generate scoring events that form combos, with achievements and a full leaderboard, stats that track to your profile, branding/login/title screen, etc, in about 10 minutes.
Wow, Matthew. Exactly the kind of answer I was looking for! Thanks!
Looks like I’m taking a similar approach; I just need to flesh things out a bit. Here’s the scene Hierarchy of the project I’m currently working on:
(A little ad-hoc and messy, but it’s getting there.)
As for a scripting approach, I’m attempting to do an more game-oriented variation of MVC; I don’t want to say too much until it’s working, but it’s based around:
Abilities, which hold stateful data and automatically send On… messages whenever that data is changed.
Reactions, which react to On… messages for abilities, usually by telling a different Ability to do something.
Currently, I also have a handful of Complex systems also (like your ScoreKeeper).
As for the distaste for “Actor-Update” approach, I’m basically saying that your messaging system is exactly the cleaner type of approach I like. The type of system I dislike is one where everything is done based on calculations in the Update function of each behavior. The main drawbacks are:
Indeterminate Update Order: You never know what order the Updates will happen in so scripts need to be written in a more manner than will work whether the object you’re checking/relying on was updated earlier or later.
Non-Inherit Change Detection: There often isn’t an inherit way to see if something you’re relying on has changed without checking its value every update against a cached value.
Slower: Updates that early out still take time and checking values of other objects takes more work, even with caching.
Leads to Non-Reusable Scripts: The more work one does from an Update function, the more it’s tied to other objects. This can be avoided, but it’s all too easy to just reference everything you want from an Update function.
I think you can see where I’m going here. My goal is to make a game with zero game-specific Updates in it, all Updates and FixedUpdates would be in highly reusable scripts along the lines of animators. As for the one-way dependancy thing, I basically mean not having a lot of cross-dependancies. My current set-up follows this to a rough extent with:
completely reusable Libs/Animators depend on nothing (other than the Unity libraries)
somewhat reusable Abilities depend only on Libs
somewhat game-specific Reactions depend only on Abilities
mostly game-specific Complex systems depend on pretty much everything (currently, but I’d like to find a better approach)
There are no limitations. Really, most of the time I barely use Update for anything except polling input. There are cases where it’s appropriate, but mostly coroutines are simpler.
Our later projects make very little use of Update/FixedUpdate. We’ll use them if a value is constantly interpolating towards a target or if we absolutely have to poll some value, but usually we don’t.
What we try to do is chain everything to the most relevant entry point. Take Crane Wars–we use triggers to determine if a building piece is stacked. That OnTriggerEnter() entry point does some logic, which talks to some other manager, which eventually fires a MessageFloorAdded message, which other scripts (scoring/etc) process. We don’t have to poll anything because the event will bubble up and broadcast a message to anything else that cares to listen.
Coroutines are another way we avoid Update. The main loop for our tower crane AI is just:
/**
* Our primary series of events
*/
function MainLoop()
{
while(true)
{
yield UpdateNearby();
if(CanAttack())
yield Attack();
else
yield Build();
yield WaitForSeconds(0.25);
}
}
Both Attack() and Build() can take as long as they need to–it might be instant, if there’s nothing to build, or it might be several seconds or more.
In turn these high-level functions just wait on lower-level coroutines: yield StackOn() in turns yields for Pickup(), SetTarget(), and WaitForCalmHook(), and so on. This is much easier than juggling states in a single Update().
Your image is broken so I can’t comment on your specific project. I will say it is definitely possible to over-worry about reusable scripts to the point where you never finish your project. If you never build a second game, and a third, reusable is kind of a moot point. Don’t make them so reusable that you can’t even use them once…