• entries
    23
  • comments
    52
  • views
    33500

Lessons learned while switching to Unity

Sign in to follow this  
FishingCactus

4193 views

[font=inherit]epistory_c_to_unity2.jpg[/font]

Paradigm Shift
When we started working on Epistory, we had to choose whether to use our proprietary engine or not. For reasons that go beyond the scope of this post we decided to go with Unity. While the prospect of working with a tool as streamlined as Unity was stimulating, after five years working in a workflow dominated by C++ my C# habits were rusty if not inexistent.
After some time with C# I remembered and saw some of the neat tricks you can do with this language. I also received and read a few tips for Unity itself that can do wonders to keep performance high and coding time low. I will keep this as code free as possible and direct you to the relevant documentation -if necessary- to get all the juicy details that would needlessly blow up the length of this post.

Stay organized
While Unity is very flexible and lets you do basically anything, it can be a blessing as well as a curse. If you don't force yourself to organize the project and the code from the start, it will become messy really fast. One performance hit that is negligible at the beginning but can grow into a big problem later down the road is the caching of your GetComponent(). Basically each time you ask for a specific component in a GameObject, Unity will go through its component list. In most cases you can safely cache the result and keep a reference. If you start adding components at runtime you'll have to decide whether to cache it or not.

Leave no warnings behind
Even though most programmers will treat warning as error -or at least minimize the amount of warnings- it bears repeating. The more serious warnings are almost always a bug waiting to be triggered. That is even more important in C# because of some leeway given to the developer. For example: you can hide a virtual function if you don't explicitly add the override keyword to the sub-class function declaration. And a warning will remind you to make your intentions explicit. The difference between overriding and hiding is that the overridden function will call the runtime type and the hidden function will call the compile-time type.

False friend
The switch statement is a good way to keep the code readable. But in this case its behavior is slightly different in C#. You cannot fall through to the next case section. You have to place a break/return/goto... However, there is a walkaround. You can use something like "goto case 1;" to jump to another case. More details here

Missing Link
LINQ can be a powerful tool to interface a program seamlessly with a database. Even though its syntax can be off putting, you should at least try it before you leave it. You can use SQL-like statements to query an xml file, for example. You can also use it to perform operations on IEnumerable (a.k.a. Arrays and Array-like) classes. All you can eat buffet here


screenshot_epistory_prog2.jpg

Daily routine
Coroutines can be achieved in pure C# but Unity made their use very easy and intuitive. It is akin to starting a new thread without the problems associated with thread safety issues like concurrency, race condition & deadlock. The coroutine also behaves like any other member function. It has access to other functions and member variables.
I will leave the implementation details aside (see links below) but know that it can easily be used to provide easing to an object over time or calculate the next score increment. Another, more advanced, use-case is a very elegant way to implement a state machine. More information here and there and state chartshere

Eventful delegation
Event firing and registering is built into the language. Events & delegates are two sides of the same coin. The delegate provides an equivalent to an array of function pointers and the event is the message being sent. This makes for painless event driven programming and we all know how much a game can be event heavy.
This could make a post topic by itself so I leave you with the documentation and an in depth tutorial/study

Epilogue
There you have it. A non-exhaustive list of tips, tricks and gotcha. Thank you for reading and feel free to ask any question in the comments.

Sign in to follow this  


10 Comments


Recommended Comments

So basically C# is one of the best programming languages out there (along with C++, Java, Ruby, Phyton). And Unity does not break it. The switch statement in C# is great. You can fall through in this case:  case A: case B: // do stuff for A or B. Goto statement available where it shines. I think, it is the same in Java. Yeah C# has delegates. Never done any GUI in windows.forms? I hoped to see delegates nowhere else. Sometimes I just want a simple pointer to a single function or maybe a single object. Why do they force a collection on me?

What is off puttig about LINQ syntax? It is sooo close to SQL and JQuery as possible and better. This one I would miss in C++. Java has it. The dynamic languages probably can do similar things.

Probably there are other well written frameworks which play well with C++ language idioms.

Share this comment


Link to comment

While Unity ain't bad, and C# is great.  One of the things you should be really careful about is Unity's implementation of C#.  A lot of extra garbage collection happens on some fairly innocuous things.  For example, I wouldn't use IEnumerable for arrays.  In fact, avoid ForEach in general, as it causes garbage collection. 

(http://www.makegamessa.com/discussion/1493/it-s-official-foreach-is-bad-in-unity)

 

Now, that said, maybe they've finally fixed it in Unity5.

Share this comment


Link to comment

While Unity ain't bad, and C# is great.  One of the things you should be really careful about is Unity's implementation of C#.  A lot of extra garbage collection happens on some fairly innocuous things.  For example, I wouldn't use IEnumerable for arrays.  In fact, avoid ForEach in general, as it causes garbage collection. 

(http://www.makegamessa.com/discussion/1493/it-s-official-foreach-is-bad-in-unity)

 

Now, that said, maybe they've finally fixed it in Unity5.

 

If you read that thread in it's entirety, it's not really so cut and dry. While the bug does exist, depending on the platforms you are targeting, you might not even notice the bug. In reality, mobile platforms are likely the only place you would notice anything due to their CPU and memory constraints.

Share this comment


Link to comment

 

While Unity ain't bad, and C# is great.  One of the things you should be really careful about is Unity's implementation of C#.  A lot of extra garbage collection happens on some fairly innocuous things.  For example, I wouldn't use IEnumerable for arrays.  In fact, avoid ForEach in general, as it causes garbage collection. 

(http://www.makegamessa.com/discussion/1493/it-s-official-foreach-is-bad-in-unity)

 

Now, that said, maybe they've finally fixed it in Unity5.

 

If you read that thread in it's entirety, it's not really so cut and dry. While the bug does exist, depending on the platforms you are targeting, you might not even notice the bug. In reality, mobile platforms are likely the only place you would notice anything due to their CPU and memory constraints.

 

Eh, even in the thread, the consensus is mostly, "avoid linq and foreach in upate/fixedupdate", except for one guy, and he even predicates his LINQ love depending on the data and because of it's Lazy evaluation.  I think it's worth pointing out the bug, and noting that for a lot of foreach calls, a simple for() replacement is very simple to do, comes at little cost of readability, and if done upfront, gives a nice small speedup, and will be less of a headache later if one ports to mobile, or suddenly has to deal with a shitty PC.

Share this comment


Link to comment

 

 

While Unity ain't bad, and C# is great.  One of the things you should be really careful about is Unity's implementation of C#.  A lot of extra garbage collection happens on some fairly innocuous things.  For example, I wouldn't use IEnumerable for arrays.  In fact, avoid ForEach in general, as it causes garbage collection. 

(http://www.makegamessa.com/discussion/1493/it-s-official-foreach-is-bad-in-unity)

 

Now, that said, maybe they've finally fixed it in Unity5.

 

If you read that thread in it's entirety, it's not really so cut and dry. While the bug does exist, depending on the platforms you are targeting, you might not even notice the bug. In reality, mobile platforms are likely the only place you would notice anything due to their CPU and memory constraints.

 

Eh, even in the thread, the consensus is mostly, "avoid linq and foreach in upate/fixedupdate", except for one guy, and he even predicates his LINQ love depending on the data and because of it's Lazy evaluation.  I think it's worth pointing out the bug, and noting that for a lot of foreach calls, a simple for() replacement is very simple to do, comes at little cost of readability, and if done upfront, gives a nice small speedup, and will be less of a headache later if one ports to mobile, or suddenly has to deal with a shitty PC.

 

Yeah, I agree with you for the most part. A bug is a bug, and that is hard to argue, but I am personally not going to worry about it until I see some real world benchmarks illustrating the performance hit.

 

Also, IIRC, LINQ is only partially implemented in the version of Mono that Unity is using and that is one of the biggest reasons to steer clear of it in my opinion.

 

I am sure that you have seen this, but it is the latest official thing that I have seen talking about a Mono upgrade. 

 

http://blogs.unity3d.com/2014/05/20/the-future-of-scripting-in-unity/

 

In any case, lets hope that IL2CPP lives up to the hype when it is fully implemented.

Share this comment


Link to comment

Linq was one of the game changers that was added to C#, it transforms how you think in the language. I have a whole set of monadic comprehensions implemented that integrate with linq. Maybe, IO, State, Validation and Exception. Linq is not tied to IEnumerable<T> it is just the only monad they supplied out of the box for use with it.

 

This for example if my validator to test a spawn location

        public static IValidation<Level> CanSpawnActor(
            this Level level, long actorId, Vector location)
        {
            return
                from isNew in level.IsNewActor(actorId)
                from targetTile in level.IsValidLocation(location)
                from validMove in targetTile.IsValidMove()
                select level;
        }

This will return the level object if valid or the reason the spawn is invalid when not

 

Select = M<A> => A -> B => M<B>

SelectMany = M<A> => A -> M<B> => M<B>

etc

 

And the comprehension engine is a monadic parser

Share this comment


Link to comment

"False friend
The switch statement is a good way to keep the code readable. But in this case its behavior is slightly different in C#. You cannot fall through to the next case section. You have to place a break/return/goto… However, there is a walkaround. You can use something like “goto case 1;” to jump to another case. More details here"

 

I really don't like how the switch operates in C# / Unity. It always feels like it is unclean code for some reason but sometimes, it's really the way to go (especially in Unity).

Share this comment


Link to comment

I would've preferred they had some sort of fallthrough keyword, or perhaps use the 'continue' keyword that already exists for loops -- though that might be problematic.  I do like that not having any break or anything bubbles up to the compiler, as it's nice to catch those kind of sneaky errors at compile time, but it is uglier when you have to bust out gotos in a switch.

Share this comment


Link to comment

You can fall through in C# but not cases with code in them. This is valid

 

switch (someValue)

{

    case 1:

    case 2:

        // do some work

        break;

 

    case 3:

        // Some more work

        break;

}

 

if you have common code for a few switches then extract it to a function :)

Share this comment


Link to comment

Needing a goto in a switch shows bad code structure imho,

 

The C style fall through is also a common source of bugs where a forgotten break can cause real issues.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now