A Fun Tower Defense Game
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Assets
Documentation
ProjectSettings
UnityPackageManager
.gitignore
ReadMe.md

ReadMe.md

Fun Tower Defense

This is a simple "Fun Tower Defense" (FTD) game.

Features

  • Unity 2017.x
  • C# Programming
  • Custom MVC Architecture
  • Mostly new code
  • Dependencies: Thanks to DOTween and Bendangelo for some helpful existing code.

(Note the RMC namespace is mine too and refers to the name of the GIT repo).

Getting Started

To Play Game

  • Open Unity 2017.x
  • Open ./Assets/Scenes/Main.unity
  • Press Play
  • Enjoy!

To Increase Difficulty

The game is pretty easy by default. This is to help illustrate the core project concepts without frustrating the reviewer.

Want increased difficulty? Use the Unity inspector to view and update the instances within ./Assets/ScriptableObjects/ModelConfigs/.

To View Source

  • Open ./Assets/Scripts/Runtime/RMC/Projects/FTD/MVC/FTDBootstrap.cs

Architecture

Classic MVC

The architecture of MVC divides the core project elements into 3 tiers; Model, View, Controller. This separation of concerns is a helpful tool to support celebrated principles of readable, scalable software including decoupling, SRP, and KISS.

Model

  • This contains the app's data. Exposes a public API to the data.
  • It knows nothing about the larger context of the application.
  • Dispatches events (e.g. when data changes)

View

  • It displays the app's data (e.g. renders graphics and audio)
  • This knows little about the larger context of the application.
  • Listens to events (e.g. when data changes), but does not change the data. Dispatches events (e.g. when the user clicks the screen)

Controller

  • This Manipulates the app's data (e.g. reduce player health). It also connects the user's input from the View into changes in the model.
  • It knows much about the larger context of the application.
  • Listens to high-level events (e.g. when the player dies) and translates that into meaningful consequences

Modified MVC

The classic approach to MVC was not created with Unity in mind. In FunTowerDefense some modifications to the architecture exist...

Model

  • Each BaseModel subclass (which holds runtime state) may optionally reference a BaseModelConfig (which holds easily tunable settings) of type ScriptableObject. For example the TurretModel refers to TurretConfig.

View

  • The BaseView extends MonoBehaviour. For example, the TurretView sits on the TurretPrefab. Of the MVC concerns, the View is the most closely connected with Unity.
  • In theory, this can run outside of the MVC (such as a standalone demo/test scene for artists to preview). Complying with this is optional, but it is helpful to have some such scenes.

Controller

  • This knows about the Model but not the View. For example, the TurretController is called by the TurretView but not vice versa. Some other MVC frameworks agree, but many do not. This is an experiment and has pros.

Sample Workflows

This custom MVC facilitates the roles of several team members.

Game Designer (Model Config)

The workflow is very smooth for a game designer to play the game and tweak tuning values. See images above. Some recommended steps...

  • Create a new config Unity -> Assets -> Create -> FTD... or duplicate an existing config.
  • Select an existing config in ./Assets/ScriptableObjects/ModelConfigs/
  • Modify config values through Inspector
  • Bonus: With light programming skills, the Game Designer can update the BaseModelConfig subclass. For example, set new C# defaults, validate values with OnValidate(). With more programming skills the Game Designer can add/remove values and integrate them into the related BaseModel subclass or other MVC concerns.

Artist (View)

  • The system supports liberal use of the helpful Prefab concept. Artists are free to update the visuals of the prefabs; models, particles, textures.
  • Thanks to how the View is set up. As needed, a prefab can be run in its own standalone scene (outside of MVC). For example, a fictitious MainCharacterPrefab can run in the Main.unity scene with full functionality, yet in a standalone scene still be previewable (e.g. showing idle breathing animation. Previewing in a standalone scene aids rapid development.
  • Bonus: With light programming skills, the Artist can update the BaseModelConfig subclass related to their related View. This aids meaningful dialog with other team members about programmatic variations to the art. For example, a fictitious MainCharacterPrefab could have one unchanging look from an FBX model and related texture, yet the character's hat color could be fed programmatically from the config.

Programmer (Model / Controller)

  • Once the project is setup, adding new features is so easy. It's remarkable that once the worry about 'how' to add a new feature is removed (because of the prescriptive nature of MVC), adding features becomes a lighter and more playful experience for the programmers.
  • The significant things that happen in the game (events) are so much more open, any system can react to them. An example is that once a Turret takes damage (a feature that required for a core mechanic), any other system (audio, particles, UI) can easily tap into that event to add polish. Furthermore, responsibility for such polish can be delegated effectively to the team.
  • With a high-level overview of the project, the Programmer can maintain and update all code and assets more easily thanks to the separation of coding concerns and thoughtful separation of code from assets.
  • Bonus: The MVC framework's documentation and consistent approach help onboard new junior and senior programmers to the team.
  • Bonus: Want to add a new MVC element? Copy about 5 classes from ./Assets/Scripts/Runtime/RMC/Core/Templates/MVC/ and to get started easily.

Architectural Musings

Here are some musing about MVC in general and about this custom MVC implementation.

Pros / Benefits

  • Easy onboarding of new staff and easy discussion about projects with common workflows and shared vocabulary and design-patterns.
  • Easier refactoring since the project is testable and since modifications never modify the whole project monolithically.
  • The separation of coding concerns helps projects scale.
  • Easy to have multiple views of the same data (e.g. in-game experience, overhead map of objectives, and character inventory)
  • The design is prescriptive. The solution for adding a new feature or a whole new system is known. Having the codebase follow a consistent structure aids each member of the team to be effective.

Cons / Solutions

  • Like any architecture, there is a learning curve to MVC.
  • It takes 'many classes' (about 5) to add a new MVC Element (e.g. Turret) to the game. To speed the process one can copy from ./Assets/Scripts/Runtime/RMC/Core/Templates/MVC/or copy the classes of an existing similar system.
  • The custom MVC framework uses UnityEvents almost exclusively (vs C# events or other options). I started with this thinking it would help Game Designers and Artists to perform some non-programming tasks more easily (e.g. wire up a button to an existing method using the UnityEvent in the Unity Inspector). That is still possible, but I did not end up doing that at all. See more thoughts on C# Events vs UnityEvents.

Possible Future Features

  • The custom MVC framework does not distinguish between Models with a view and those Models that don't have a view. Idea: Some systems treat them uniquely and solve the latter with a ViewModel. That could have benefits.
  • The custom MVC element lifecycle is stable but has limitations. For example, several Views that are instantiated on the same frame cannot access each other predictably. Due to this small-scale FTD, this was not an issue. Idea: Expose something like Locator.OnViewAdded<V>().AddEventListener(l) so a View can wait properly (a frame?) for predictable access.
  • The Locator works and properly prevents undue access to other concerns. However, adding an Inversion-Of-Control (IoC) system could give much more flexibility. However, IoC convolutes the lifecycle readability. It's more challenging to intuit when an injected reference lives and dies. See IoC for more.
  • Using interfaces more liberally would decouple the contract between concerns (e.g. between TurretView and TurretController, a new ITurretController could be added).
  • The custom MVC has a concept of EventManager and GameEvents for general messaging. This works really well. However, replacing it or adding to it with a Command Pattern would offer valuable features like queuing, reversing, and replaying Commands.
  • How will this approach fit with the new Unity 2018 ECS Feature?
  • How will this approach fit with the new Unity 2017 Timeline Feature?

The case AGAINST MVC with Unity

In the interest of full disclosure, many junior and experience programmers oppose using MVC for Unity.

  • MVC is designed for 2D GUI of data-driven apps, and not for 3D games. It does not scale to the complexities of multimedia and 3D.
  • MVC's View is theoretically not stateful with data. However, in Unity, the view (especially GameObject and MonoBehaviour) has its concerns spread into Model and Controller. So much of Unity's view's value is its data model (in particular gameObject.transform) and its controller functionality (e.g. physics, collision, and more).
  • MVC does not work well with Unity's traditional Entity-Component-System. This is similar to the last point. I'm not speaking about the new Unity 2018 ECS, but the classic ECS that is as old as Unity; aka use of GameObject.

Testing

Challenges

There are a few classic challenges with the unit-test testability of Unity Games.

  1. Games are different from many other types of software because a good amount of the code relates to 2 areas; handling input and rendering UI (3d models, 2d sprites, audio, etc...). These 2 areas are notoriously hard to test.

  2. Unity games rely heavily on UnityEngine.MonoBehaviour (i.e. The "GameObject"). At runtime, we depend on its execution model (e.g. Awake, Start, Update), which do not perform predictably in isolation during edit-time testing.

Solution

With this Custom MVC solution, we can isolate the more test-friendly concerns to help us test better. A beneficial workflow of test-driven-development (TDD) in FTD begins with testable Models.

The project includes Unit Testing ./Assets/Scripts/Tests/ which focuses on the Turret element.

In production, its recommended that the team set a goal for code coverage (e.g. every public method of every subclass of BaseModel, BaseController).

RMC.Core.Proxies

I created this package to remove SOME of the dependency on UnityEngine.GameObject. Now the IWorldTransform and its Position are used instead.

A larger, future refactor could further reduce dependencies on UnityEngine. However, striking a balance between ease-of-use (more UnityEngine) and testability (less UnityEngine) is recommended. Working with Unity instead of against it has relative advantages.

Interestingly, because I refactored to add the `RMC.Core.Proxies' after the game was already complete and working, a single commit b7b16c1a7b290121a28a2265dd02d310b33e47d6 is available to study impact on the codebase. Very fun. Check it out!

Coding Standards

This project complies with RMC coding standards and includes templates at ./Assets/Scripts/Runtime/RMC/Core/Templates/ for use as new classes are created. It is recommended that each team develop their own policy and buy-in (or not) regarding coding standards. See more about coding standards.

  • The flower-boxing is admittedly verbose. The spirit is to encourage consistent ordering of new features added over the lifetime of the codebase.

References

Thanks to the thoughtful and generous community around software (especially Unity) for contributing to free and open learning resources.

Other

MVC / Unity

Created By