Categories & Tags
Archive

Unit testing part 2 – Unit testing MonoBehaviours

June 3, 2014 in Technology by

As promised in my previous blog post Unit testing part 1 – Unit tests by the book, this one is dedicated to designing MonoBehaviours with testability in mind. MonoBehaviour is kind of a special class that is handled by Unity in a special way. Every time you try to instantiate a MonoBehaviour derivative you will get a warning saying that it’s not allowed. Being a good boy-scout and not ignoring the warning (ignoring a warning is bad in the long term!) you might have asked yourself the question, how can I mock MonoBehaviour then? The good news is that you don’t have to! Let me introduce you to…

The Humble Object Pattern

If you’ve already tried to write tests, you’ve probably stumbled upon some of the natural enemies of unit testing like UI, legacy code, bad design with no source-code access or areas with a high degree of concurrency. What make these parts hard to test? Achieving isolation: separating what is being tested from the context. There are tools out there that can help for legacy code, but for new code a very simple pattern can be used: The Humble Object Pattern.

The idea behind this pattern is very simple. Whenever you want to test a component that has any dependencies that are hard to test, extract all the logic from the component to a separate, decoupled (thus testable) class and then reference it. In other words, the problematic component (with a dependency that makes test authors’ lives miserable) becomes a very thin layer of code that has as little logic code as possible with all logic operations delegated to the newly created class.

From a state where the test has an indirect dependency to the untestable component…

example-dependancy1

…we got to a state where the test is not even aware of the bad (well, just untestable) code:

example-dependancy2

That’s pretty much it. It’s a no-brainer to be honest.

Games vs testability

What makes games so special in term of code and testability? How is testing games different from testing other software? Personally, I consider games as a pretty sophisticated pieces of software. It would be naive to say games aren’t that much different from the software you use every day. In games (with exceptions of course) you will find shiny and polished graphics, background music and other well-engineered sound samples. Games often need to handle realtime input, potentially from a variety of sources, as well as a range of output devices (read resolution). Non-functional requirements can be also more strict for games. Multiplayer games will require you to have a reliable, synchronized network connection while, at the same time, keeping the performance you need to maintain a constant frame rate.

This can make for a complex system that touches on many different kinds of media and technologies. For me, games were always masterpieces of software end-product, with some of them aspiring to be recognized as pieces of art (in the classical, visual way as well as the technical, behind-the-scenes side).

Unity vs testability

All this complexity has consequences for the code architecture. To our misfortune, high performance architectures usually work against good code design, a restriction you may also encounter in Unity. One of the core mechanisms that had to be designed in a special way, is the MonoBehaviours mechanism. If you ever wondered why the callbacks in MonoBehaviours aren’t implemented with interfaces or inheritance (as common sense perhaps suggests), it is for performance reasons(See Lucas Meijer’s clarification in the comments). Without going into detail, this also works against the testability of MonoBehaviours. The fact that you can’t instantiate a MonoBehaviour with the new operator pretty much prohibits you from using any mocking frameworks out there. It probably wouldn’t be a good idea anyway with all the things that are going on behind the scene every time a MonoBehaviour is used. Intercepting this behaviour would generate lots of problems.

You vs testabilityexample-cat

In the end it’s all about you, and how motivated you are to write testable code. Many approaches can solve the same problem but only few will work well for test automation. If you want to write testable code, sometimes you will need to write more code than you would think is necessary. If you are still learning (shouldn’t we be learning our whole life, anyway?) or just got on the test automation adventure path, you may find some of the code pieces or design assumptions as an unnecessary overhead. These, however, become a habit so quickly that you will not even notice when you start using the pro-automation designs without even thinking about it.

In this blog post, I promised to show you a way to design MonoBehaviour to be able to test them afterwards. It wasn’t completely true, because we won’t be testing MonoBehaviours themselves. You probably already have an idea of how to implement the Humble Object Pattern to your design to make it more testable but, nevertheless, let me show you the idea implemented in a real project.

The example

Let’s create a use-case for the purpose of this example. Imagine a simple player controller that is responsible for steering a spaceship. To simplify the example, let’s put it in a 2D worldspace. We want the spaceship to be able to fly around in every direction. It has a gun that can shoot straight with bullets (space-rockets?) but not more frequently than a given firing rate. The number of bullets is also limited by the capacity of the bullet holder so once you shoot all of them you need to reload. To make it more interesting, let’s the make the movement speed dependent on the spaceship’s health.

A Monobehaviour that will serve as a controller for our spaceship could look like this:

In the FixedUpdate callback we read the input and perform the action depending on which buttons were pressed by the user. To move around the spaceship we need translate spaceship’s position with the speed constant according to the direction of the axes. As you can see in the code, the deltaX and deltaY variables are multiplications of: Time.fixedDeltaTime, the value from the input axis and the speed constant which itself is dependant on the health level.

On the Fire1 event (e.g. left mouse button click) we want to check if it is possible to shoot the bullet. In the first place, we need to have at least one bullet left in the bullet holder. Secondly, we want to only allow the spaceship to shoot at certain rate (once every half a second in this case). Therefore, we check how much time has passed since the last bullet was fired. If we’re good to go, we spawn the bullet.

The Fire2 event will simply reload the bullet holder.

To write unit tests for this logic, we need to overcome two problems. The first one, as previously mentioned, is the non-mockable MonoBehaviour class on which we depend on via inheritance. The second problem is more general for real-time software. Our logic is dependent on time (firing rate) which makes it impossible to perform unit tests since we can’t intercept the static Time class from Unity. The good news is that all this can be solved.

Let’s refactor our code a bit by applying some simple method extraction refactorization and keeping in mind that the logic methods should not reference the Unity API (Input handling and bullet instantiation in this case). The time dependency in the if statement, should be extracted to a separate method as well. The final result should look more or less like this:

example2

As you can see, the FixedUpdate method here does nothing more than passing on the input from the users to the method that does the the logic part. The firing rate check was extracted to CanFire method, that generate the result “true” if a specified amount of time has passed. This extraction is important as it will allows us write unit tests later. If we were able mock the SpaceshipMotor class right now, we would simply intercept the CanFire method and make it return true or false whenever we intended to. It would make the test time-independent. But since we can’t mock SpaceshipMotor because it inherits from Monobehaviour, we need to apply the Humble Object Pattern.

How do we do that? We simply need to extract all the logic code that doesn’t use the Unity API to a separate class and introduce a reference to it to the SpaceshipMotor. Let’s look at the class again and see what to extract. The TranformPosition and InstanciateBullet use the Unity API but everything else can be extracted. I know there is also the static Time class but let me get back to that later.

The last thing left to explain before we do the actual extraction is how the extracted logic communicates with the Unity API without depending on it. This is the place where the interfaces come in. The class with logic will have a reference to an interface, and I will not care about the actual implementation. To keep things simple, we can implement those interfaces directly in MonoBehaviour itself! Let’s take a look at the following 2 classes:

Example3

Example4

Let’s start with the SpaceshipMotor class. The class implemented some interfaces that are responsible for transforming the position of out spaceship and instantiating the bullet respectively. The class itself got a field that refers to the SpaceshipController which implements all the logic now. The SpaceshipController class knows nothing about the SpaceshipMotor and the only thing it can do is it invoke methods from the interfaces it references.

Unity won’t serialize references to the interfaces. If you don’t care about serialization, simply pass the interface references while constructing the SpaceshipController class. Otherwise, you can set the references in OnEnable callback that is called every time after the serialization happens. Just for the record, the whole SpaceshipMotor class will be serialized in the usual way, it’s just the interface references that will be lost.

You must have noticed the Time class reference in SpaceshipMotor. I know I said there should be no Unity API reference in here but I left it there to demonstrate a different approach for handling time dependant dependencies. Ideally, we could simply pass the Time.time value as an argument to the methods.

For UML fans, this is the end result as a (simplified) UML diagram:

example-uml1

Unit tests

With the decoupled SpaceshipMotor class there’s nothing preventing us from writing some unit tests. Take a look at one of the tests:

Example5

The test validates that you can’t fire if you have no bullets left. The test itself is structured according to the Arrange-Act-Assert pattern. In the arrange part we create object mocks with GetGunMock and GetControllerMock methods. The GetControllerMock, besides creating a mock, overrides the behaviour of CanFire method to always return true. This removes the time dependency from the controller object. Next, we set the current bullet number to 0. After that, we apply fire to the controller class and we assert if Fire has not been called on the gun controller interface.

There are few more tests in the project. You can grab it from here and play with it bit. I used NSubstitute for the mocking object. We also ship a version of it with the Unity Test Tools. All of the three versions of the controller we discussed here are attached in the project.

That’s it from me today. I hope you enjoyed the read, and happy testing!

Tomek

Share this post

Comments (31)

Comments are closed.

3 Jun 2014, 2:08 pm

Good stuff m8! i recently bought the book “The art of unit testing” see allot of similarities!

Peter
3 Jun 2014, 2:10 pm

It just overcomplicates the code to get these tests running and you picked some fairly easy stuff. Just make sure not to make any mistakes during development, so you don’t need tests at all ;)

Richard Fine
3 Jun 2014, 2:45 pm

Yeah, honestly, this looks like a really poor solution in the long run. Double the number of classes in my project, and a load of ‘wrapper’ code just to couple it up to Unity’s component hooks… I can’t imagine ever using this on a large project, which is exactly where the need for tests becomes more pressing.

3 Jun 2014, 3:21 pm

Great post ! It’s cool that you get down to the actual details.

I learned something new (Humble object, in fact it’s a pattern from the xUnit test patterns book. Probably should have a read as i’ve only read “The art of Unit Testing by Roy Osherove).

One issue though is that most fields you declare are public (not sure whether that’s for testing purposes of for unity serialization). For bigger complicated projects this can be an issue.

Other than that, i agree that the logic should be extracted from the MonoBehaviour (who said you should implement the logic directly in there anyway?)

I think i will update my blog post about Unity Test tools to promote this kind of usage instead of the quick n dirty example i gave there.

Tomek Paszek
3 Jun 2014, 6:12 pm

@Peter
I hope you’re just trolling
@Richard
Writing testable code can be always seen as an overhead. So could be using managed languages. I’m not quite aware of the performance impact of having (too) many classes/files but I’d be happy to hear about it.
In my opinion, having a ‘proper’, decoupled and auto-tested design, nevertheless the overhead it may give, will be more beneficial in larger project which tend to be hard to maintain and introducing regressions is much more likely.
If you need to look for performance improvements (I guess this is your main pain point) I wouldn’t focus on the ‘number of classes’ in the project as the first thing to look at.
@Lior Tal
The fields are public because I took a shortcut and wanted to use the inspector to manipulate the variables. You are correct that they should not be exposed like that.

Nezabyte
3 Jun 2014, 7:56 pm

Awesome! Loving these unit test articles, and it’s a nice way to tie in the advice from the Art of Unit Testing book. Though it does require extra steps to make something testable, I think it’s worth it and actually encourages you to write less coupled code. Plus I’m able to refactor code as needed without worrying about have to re-test everything. Are there more unit test articles planned for the future?

Richard Fine
3 Jun 2014, 10:08 pm

@Tomek: No, I’m not worried about runtime performance – the runtime cost of having the wrapper is very small. I’m worried about developer productivity.

You’re asking me to double the number of classes I have to keep in mind at any time; to double the number of pieces of code I have to alter if I’m doing something like handling a new MonoBehaviour message; to open up a whole new layer of opportunities for bugs (e.g. accidentally calling the wrong function overload in the wrapper). And that’s not even taking into account things like all the extra interfaces it introduces to scope.

And let’s not forget that this doesn’t let me test anything that touches the Unity API directly; not only does that leave a lot of my 80K lines of code untested, but it also forces me to strongly segregate Unity-API-touching code from non-Unity-API touching code, which can distort the semantics of things very badly.

Sacrificing so much simplicity for the sake of testing is not a win. Remember, testing is not an end in itself: I only care about having automated tests because they help me fix problems, but if implementing them makes it harder to actually develop those fixes then it wipes out the benefit of having them.

3 Jun 2014, 11:35 pm

@Richard
“You’re asking me to double the number of classes I have to keep in mind at any time; ”

It’s called software engineering.. a good solution is not the one with minimal # of classes…

Regarding tests that touch the Unity APIs – by its definition, a unit test should not involve too many things (some would argue it should test the scope of 1 method without any external dependencies). As such, for a test that requires the interaction with Unity APIs, you could do a different type of test (e.g: Integration Test).

Richard Fine
4 Jun 2014, 1:16 am

@Lior: A good solution is one with low complexity and high readability/maintainability. It’s not about the number of classes, it’s about the number of separate moving parts that have to be taken into account any time anyone is reading, extending, or maintaining the code. If I saw a large-scale project making heavy use of the pattern described here then I’d say it’s called software overengineering.

Of course I can do a different kind of test – I already do, it’s not like I have 80k LOC and just ship it without testing :P – but ‘do something that isn’t a unit test’ is irrelevant to the problem of ‘how to do unit tests.’ There are plenty of situations where I could write a unit test for a single method that checks for particular interactions with the Unity API, were it not for the fact that I can’t easily intercept those interactions.

David Mitchell
4 Jun 2014, 5:35 am

I’m with Richard on this one. I think this approach, but I feel your code should not be designed around a particular unit test approach, it should be designed around the best solution for a given problem.

Tomek Paszek
4 Jun 2014, 11:20 am

@Richard,
Applying a design pattern in order write unit tests for the code is definitely not overengineering. It’s done for a purpose, not for the sake of using design patterns.
Design patterns were defined and named in order to make reading and maintaining the code easier. Once you know (from your colleague/naming convention/comments) a certain pattern is used in your code, you don’t need to think about how or why such code structure is being used because you know that from the pattern definition.
Last thing is that if the “separate moving parts” are decoupled, and don’t depend on other code, they ARE easier to read and to maintain.

PS. If you want to test your code interacting with Unity API you can use the integration test framework.

@David,
It depends on your definition of the best solution for the problem. For me the best solution is the one I can test in automated way. It’s the only way I can be sure the code works as I expected and leaves me with clear conscious when I make changes to the code.

Tomek Paszek
4 Jun 2014, 1:27 pm

@NEZABYTE
Yes, we do plan to have more articles. Some of them are already in progress and for the other, we still need to decide on what to write about :)

chris
4 Jun 2014, 3:43 pm

I think Richard is being far too kind. Complicating your code to make it fit into some testing method isn’t putting the cart before the horse, it’s shooting the horse and feeding the cart carrots.

Thomas Petersen
4 Jun 2014, 5:22 pm

@Chris: I think both you and Richard are missing the point a bit. The solution Tomek outlines is because of the MonoBehaviour implementation, not because of unit tests or patterns. There simply is no way to test a Monobehaviour as a unit test, because you are not allowed to use the constructor, so you have to isolate the logic from it first.

I’m kind of amazed that you think it is complicating matters more. Personally I think the code you get from mixing the MonoBehaviour with any form of logic leads to a really horrible mess, where the mix of event handling (Input., Update, FixedUpdate etc.) and state (all of the SpaceshipController) makes it super confusing to trace the code. In the above example there is done little more than a 1-1 interface on the original MB class, extracting all state and logic to a separate class and delegating calls 1-1. But I guess that is just me.

4 Jun 2014, 7:00 pm

@Chris – regarding what you wrote “complicating the code to make it fit into testing method…”

Here’s a question i asked a while back about “whether its OK to modify your code to be more testable”: http://programmers.stackexchange.com/questions/199090/is-it-a-bad-practice-to-modify-code-strictly-for-testing-purposes

I think it’s simply a cost/benefit consideration – what’s efforts and complexity you are adding to your code vs. what benefit you get out of it.

Regarding what @Richard said – you raised the point about the number of classes, i was merely giving an example that it doesn’t really matter. Also, you won’t have to write x2 classes as there doesn’t have to be a 1-1 relationship between logic and monobehaviour classes really.

I also agree that this solution is not a silver bullet, and it should be available together with API changes to make the Unity API more testable (and of course, new APIs should be exposed more testable from day 1).

Richard Fine
5 Jun 2014, 12:24 am

@Tomek: It’s true that this decouples things in terms of code dependencies – i.e. you can compile SpaceshipController without SpaceshipMotor being present – but in practice it’s never going to decouple things in terms of semantic dependencies. IGunController and IMovementController, ideally, would be ‘designed’ in their own right, but what happens is that they become dumping-grounds for whatever their clients need. And very quickly you have a situation where things are technically decoupled but nobody can actually understand what a piece of code is doing without reading the implementations of the other pieces as well. (Which then takes longer because they’re scattered around).

Also, as I said to Lior: I know I can use integration tests. But that’s giving up.

@Thomas: I get that this is being proposed as a way to work around the inability to test a MonoBehaviour. But that’s exactly what it is, in my eyes: a workaround. It’s a solution, but not an ideal solution by any measure. And given the added complexity it brings to a codebase to fracture everything in this way, not really a viable solution for most projects, I think.

And you’re doing a serious disservice to my coding abilities if you think I’m haphazardly mixing event handling and state management across most of my classes :P I already have a strong segregation of responsibilities across my classes, and I do use something like this pattern in the situations where it improves quality.

But we’re not just talking about implementing Update() here, we’re talking about any API access at all. I can’t even write an isolated unit test for, say, a method that takes an array of GameObjects and returns the closest one; first I have to retrieve transform.position for each object and pack it into an array – which I have to allocate – and then to run the test I have to spin up the entire engine and load a test scene with a bunch of actual GameObjects in.

It’s the same constraints that we face when we’re writing multithreaded code. If we want to run code in a (separate thread | fully mocked environment) then we can’t touch the Unity API.

And there are ways around this. It’s just that they require modifications to the Unity runtime – such as the ability to have Unity-Mono create mock versions of UnityEngine.Object types, and ‘freestanding’ MonoBehaviours for testing – that you’ve not been willing to make so far…

Thomas Petersen
5 Jun 2014, 2:00 am

@Richard: I see that you actually do agree with the use of this pattern, although we all agree that it is another tool in the box to use when appropriate. Tomek is simply trying to explain that there is a way to actually achieve it, which from experience with looking at projects coming in, is not something that is generally used.

We can only agree that the testability implications of the MonoBehaviour restrictions are bad and it would be great to have that changed, but testability is not the top concern in that kind of discussion.

6 Jun 2014, 3:05 pm

@Thomas: “testability is not the top concern in that kind of discussion.”
Loved this :) !

Tomek also said: “If you ever wondered why the callbacks in MonoBehaviours aren’t implemented with interfaces or inheritance (as common sense perhaps suggests), it is for performance reasons.”
… this alone is worth a blog post!

What I’m reading here is MBs are not designed for testability, nor to follow the most basic OOP pratices, but for performance: you’ll have to be damn convincing to justify where the performance gain is!
Are you just trying to save a virtual call here? or do you just want to cull components with no Update loop? (in which case an interface with “bool needsUpdate { get; }” would totally work.)

This “Unity why u no virtual Update” thing is one of the most stumbling Unity oddities, I’m a 6+ years user and I still hate it, I’ve seen dozen of discussions about it, and yet UT has never ever stepped up to explain why, or to explain how the hell messages are implemented.
It has to involve reflection at some point, but is there any caching involved (function pointers, updatableComponents list?), or is it just a brutal “component.GetType().InvokeMember(“Update”)” every frame?
If there is caching then is it done at build-time, or at least during AOT on mobile/console? I remember catching an insane callstack a few times on iOS with a chain of 3 or 4 “Invoke_Update > Invoke_MonoSomething > BlaBla > Update”… I’m having a hard time thinking this is good perf-wise.

If the only explanation is “bad decision made years ago, sorry”, fine, but at least talk about it (or change it for 5.0…).

6 Jun 2014, 11:00 pm

I think MB callbacks are not implemented with reflection.

Another blog post (http://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/) mentions some more info about this:

“When you get a c# object of type “GameObject”[2], it contains almost nothing. this is because Unity is a C/C++ engine. All the actual information about this GameObject (its name, the list of components it has, its HideFlags, etc) lives in the c++ side. The only thing that the c# object has is a pointer to the native object. We call these c# objects “wrapper objects” …. (goes on and on)

I believe they use some sort of quick mechanism (e.g: function ptr) to invoke the callback methods on the MB. From a stack trace (native) u can see the following:

WARNING: Frame IP not in any known module. Following frames may be wrong.
0028e5dc 097a0518 0xd12dee0
0028e5fc 09780330 mono!mono_unity_thread_clear_domain_fields+0x28e3c
0028e634 0975ffda mono!mono_unity_thread_clear_domain_fields+0x8c54
0028e664 096cd6f6 mono!mono_set_defaults+0x22bf
0028e688 006df8c8 mono!mono_runtime_invoke+0×51
0028e6a4 0084a9e6 Unity!scripting_method_invoke(struct ScriptingMethod * method = 0x060151f8, struct MonoObject * object = 0x17d72798, struct ScriptingArguments * arguments = 0x0028e6f8, struct MonoException ** exception = 0x0028e790)+0xb8 [c:\buildagent\work\aeedb04a1292f85a\runtime\scripting\backend\mono\scriptingbackendapi_mono.cpp @ 199]
0028e784 0084a195 Unity!ScriptingInvocationNoArgs::Invoke(struct MonoException ** exception = 0x0028e790)+0×76 [c:\buildagent\work\aeedb04a1292f85a\runtime\scripting\backend\scriptinginvocationnoargs.cpp @ 98]
0028e794 006c80f4 Unity!ScriptingInvocation::Invoke(void)+0×15 [c:\buildagent\work\aeedb04a1292f85a\runtime\scripting\backend\scriptinginvocation.cpp @ 109]
0028e7c4 006c8449 Unity!MonoBehaviour::CallMethodIfAvailable(int methodIndex = 0n0)+0xf4 [c:\buildagent\work\aeedb04a1292f85a\runtime\mono\monobehaviour.cpp @ 530]
0028e7dc 006c8467 Unity!MonoBehaviour::CallUpdateMethod(int methodIndex = 0n0)+0xf9 [c:\buildagent\work\aeedb04a1292f85a\runtime\mono\monobehaviour.cpp @ 612]
0028e7e4 00698342 Unity!MonoBehaviour::Update(void)+0×7 [c:\buildagent\work\aeedb04a1292f85a\runtime\mono\monobehaviour.cpp @ 618]

I think they have a mechanism in place where they analyze your code to see if it contains the Update (or any other MB callback) and if you do they call it.

Having an interface for defining all callback would mean you have to define the method (even if empty) and then pay for the native-to-managed call every frame.

But this is kind of off topic…

meh11
8 Jun 2014, 8:27 am

@Benoit Fouletier, @Lior Tal:
Unity is using reflection, and it was confirmed that it’s a design mistake. See the answer by Lucas Meijer over here: http://answers.unity3d.com/questions/23830/c-overridable-methods.html

Lucas Meijer
8 Jun 2014, 10:37 pm

Your “Update” methods get invoked from Unity’s C++ code. Contrary to popular belief, there are no performance concerns with going with making things like Update virtual. We could make it exactly as fast. There are a few reasons why they are not virtual today:

- Unfortunate coupling. if we make OnColiisionEnter a virtual method of MonoBehaviour, that ties the MonoBehaviour class to the physics system, which makes it harder to build and distribute unity as seperate modules. (I have a seperate blogpost coming up about some other changes we’re doing in this area) The alternative here is to use interfaces for each subsystem. This would solve the modularity concern at the cost of introducing complexity for users new to c#. before you can make something cool happen on a collision, you now suddenly need to know what an interface is. Personally I’m gravitating towards the interface option, but am still kind of uncomfortable with it, as the we’ve always been very keen on trying to keep making scripts in unity be as accessible as possible. We are creating precedent for this in 4.6, as some of the GUI things require you to implement an interface, so it’s not unimaginable we’ll roll this out wider. (alltough the interface internally has been topic of very passionate debate in both directions)

- it would break _all_ code. each monobehaviour in every c# file, and in every dll in every unity project. This was a very big concern in the past. Today we have updating capabilities that allows us to in some cases automatically update scripts. (more coming on this in the same upcoming blogpost), so the concern is smaller, but not gone. While it’s easy to think “well just change all the things for the better”, (which is what we usually want to learn towards), changes like this significantly impact our development process in unexpected ways. example:
let’s say we make this change in unity10, and you are using unity9, and want to check 10 out during the beta, you have to update all your scripts. and now it doesn’t work in 9 anymore. suddenly a lot of people are a lot less willing to do betatesting on a new version, while “real people trying real projects” are the best sources of beta testing feedback that we actually get.

- it takes time to make. making the actual change is tiny. then we need to explain to _all_ our users why the 1000 compiler errors they get were a good idea “because its cleaner now”, update all our sample code, documentation (all languages), standard assets, then write the automatic updating feature to automatically upgrade this (for c#, and precompiled assemblies (funny enough in unityscript writing override is optional, so it would actually just work). We have a modern .NET version, GC hickups, and il2cpp, higher on the priority list today, allthough I must say that I personally very very strongly dislike not being able to write “override ” and get a dropdown list of all methods.

Lucas Meijer
8 Jun 2014, 11:59 pm

I forget the most difficult problem of making Update & friends virtual:

methods like Start, can have different returntypes today. if you make a normal start method, it has a void return type. but if you want to do something like “yield return www”, then you have to make Start return an IEnumerator. Virtual methods cannot differ in return type. I don’t know of any good solution to this, that is not “always return IEnumerator, always require yield break at the end of every start method” (other than maybe doing something funky async/await when we move to a newer .net). This is true not only for Start, but also mouse events, collision events, etc etc. this is actually a much bigger reason than the 3 I mentioned in my previous comment.

Mike
9 Jun 2014, 2:01 am

Virtual methods can return different types if the types are covariant. Not sure if that helps in this particular case.

Keld Ølykke
9 Jun 2014, 11:20 am

Thx Nevin. Unity best practices or posts that can develop them are much appreciated!

About 4-5 years ago when working at Apex we started a new project that is now http://myhorsefriends.dk/. I am a strong believer in automatic testing, continuous integration and continuous delivery, so we worked hard to get these things into the pipeline.

The solution was to separate as much as possible code from unity by making our own game entity interfaces & classes – all having an interface that matches the event methods in unity. If you put your game entities in a .dll that is NOT dependent on Unity .dlls then you can make and run tests in Visual Studio and run the tests using NUnit and similar tools.

With above approach you have the option of taking over the event bubbling too – that is – make a single monobehaviour that distributes events to all game entities. The really great thing about doing so is that you don’t have to be concerned about performance penalties in calls between Unity and your code e.g no house keeping per call and no reflection.

Doing above gets your code into a standard way of developing software. This decision is however a tough one to make, because you distance your code from the Unity-way of doing things. This distance makes you work a little extra for each event method that need to be supported and some things just wont work this way e.g. code that involves resource handling and editor scripts. You easily get on a path that gets both powerful and expensive.

Kind Regards,
Keld Ølykke

Keld Ølykke
10 Jun 2014, 3:03 pm

*Thx Tomek Paszek. Got the link from Nevin. Sorry.

11 Jun 2014, 10:47 pm

@Lucas for the sake of backwards compatibility you could always create a new API (and name it MonoBehaviour2 of course).

12 Jun 2014, 10:22 am

@Lucas: thanks for the detailed answer, eagerly waiting for your upcoming blog post(s)! I don’t like the design, but at least now I know :) !

I’m also strongly leaning towards interfaces (don’t think the uGUI thing didn’t go unnoticed!). I may be simpler in my mind that in reality, but to solve most problems (mainly not forcing code changes), I would introduce a BaseBehaviour (under MonoBehaviour), that would have none of the messages and would require you to plug interfaces. Then keep MonoBH with the current dark magic.

As for the “void Start vs IEnumerator Start” issue, I personally never use it, IMO doing an explicit StartCoroutine is very simple and much clearer. It’s different in UnityScript where just introducing a yield anywhere in a function actually changes its signature behind the scenes, but for C# I think we could live without.

17 Jun 2014, 2:18 am

I would love for the monobehavior events to be virtual because I’m terrible at remembering the names of some of them (e.g. us it OnDisable? OnDisabled? Disabled?) and their parameters. For some projects I use a MonoBehaviourBase class to declare them all just so I can override them in subclasses. I like strongly typed things!

Also, put me in the camp of people who don’t mind their code being broken in the name of progress.

30 Jun 2014, 1:46 pm

What do you think of this article about an alternative way to test Monobehaviours? Basically, the author has found a way to instantiate Monobehaviours.

Is it likely result in unexpected behaviour?

here

Tomek Paszek
1 Jul 2014, 3:39 pm

@GINGER
Hard to say how it works without trying it out but I still think that if you can separate the logic from MB, you should do it. Otherwise, write an integration test.

Chaverbeke
1 Aug 2014, 6:06 pm

Apparently i am applying this concept and i’ve run into the following architecture dilemma. Could you have a look?

http://stackoverflow.com/questions/25081663/loosly-coupling-unity3d-toenginerepresentation?noredirect=1#comment39024944_25081663

Leave a Reply

Comments are closed.