Luxel Operation | DevBlog

Development Blog for Luxel Operation's upcoming games.

What is Version Control Software?
Version Control Software (vcs) is a way of keeping track of changes made to files such as source code or binary assets (textures, models, etc.), and reverting these changes if necessary. Another strong benefit to using VCS is it allows you to work with a team of people and allow them to update to the latest copy of assets without resorting to complicated naming schemes to define which version is the latest or comparing last-modified dates when you copy files over. VCS also allows you to perform off-site backups of your assets which is extremely important in the development of any product, be it software development or video games.

VCS Allows you to track changes.
Ever suddenly realized that you can’t name anything you did in the last three days? Or realized that the bug you fixed was actually a feature and needs to be reverted, but you fixed it a week ago? Version tracking solves both of these problems and more. Version tracking allows you to compare the current version of the assets (predominantly code, though binary assets to a lesser extent) to previous versions, be it twenty minutes ago, two weeks ago or two years ago. Along with each set of changes is a set of comments put in by the developer to explain why they changed it or what the change does. This could be something as simple as “Increased friction on player movement” to something more complex like “Rewrote player movement to prevent issues like BUG#1054 and BUG#1203 where players could fall through certain parts of the DIRE_DIRE_DOCKS level.”

When comparing text files most VCS solutions will give you a detailed breakdown of what has changed since each commit – this includes which files have been modified, which lines have been removed, and which lines have been added. When comparing binary assets (such as textures or models) most software can only show that the file has been changed. VCS will allow you to revert back to a revision of the file at this point though so they still remain useful. Developer comments with each commit can help describe changes to binary assets to further aid in their usefulness.

Okay so that sounds nice and all, but I have a team, how can VCS help me?
To explain this we need to introduce some more concepts of VCS on a broad level. Revision control manages changes to a set of data over time. A copy of this data can be stored off-site (non locally, either on a server on the network or on another server on the internet) and is referred to as the “repository”. There are two common interactions with a repository, “checking in” and “checking out”. When files are first placed onto the repository a developer can “check out” the data. This downloads the latest revision (+ history, depending on VCS software) to their computer. This copy of the data is known as a “working copy” – the programmer can edit and change and modify to their hearts content but they won’t affect anyone else who is working on the data until they perform a “check in”. What checking in does is take the modifications done to the working copy and upload them to the server. Then, other developers can check out the latest version of a file.

Example:
When two developers edit the same file at the same time you can run into another benefit of using VCS. Within VCS exists the concept of “merging” changes. Example: Developer A modifies PlayerMovement.cs and re-writes the air-friction calculation and makes it much better. Meanwhile, Developer B also modifies PlayerMovement.cs but he instead works on automatically stepping up stairs instead of running into them. Developer A then checks in his changes to the repository. Developer B goes to check in his changes but discovers that the copy of PlayerMovement.cs has changed since he started working on it. Using tools built into VCS (such as WinMerge) Developer B can easily integrate the changes made by Developer A into his copy before checking that version of the file in. The repository now has the changes made by both Developer A and Developer B and no one’s work was overwritten.

If both Developer A and Developer B modified the air-friction calculation you have a slightly more complex problem on your hands. One of the developers will have to go and decide which lines from which file he wants to keep – perhaps Developer A only added in a new comment and his changes can be kept, but perhaps Developer A duplicated the work of Developer B.

Well I’m just a one-man team, I don’t think I really need VCS because no one else will be changing anything.
It turns out that VCS is still essential for one man teams. Easy backups to off-site repositories allow you to keep a copy of your data that is not on your computer. This means, if your computer gets stolen, you will still have a backup of your data. Unfortunately, in Project Zomboids case they did not have up to date external backups and they suffered from a significant setback.

Well… I’m sort of convinced, can you tell me any other reasons why I should use VCS for my next project?
Yes! There’s a ton of non-obvious benefits to using VCS. Debugging becomes much easier when your code is under version control – as soon as you notice that players can suddenly fall through a floor when they no longer could, you can now walk the revisions backwards until that bug is no longer there. Then, you can see the specific lines of code which created that bug. It’s often much quicker than manually debugging and trying to add onto the code base to fix it (since you may be patching the bug instead of fixing the real cause for it).

Ever felt unmotivated because you can’t name anything you’ve done in the last week? Thanks to VCS you can not only see what you’ve done (and your comments about it), but you can also jump back a week or two in time and see how different the build was then as compared to now! It’s amazing how much you can do in a day but be unable to specifically name as something you’ve done.

Okay, you’ve convinced me. I’ll put my next project under VCS!
Don’t wait. Better late than never – that is to say, put your current project under version control right now.

Okay, so I’m about to put my current project under revision control, what reversion control software is there and which do I pick?
Some common ones:

  • Git – Git is a revision control system with an emphasis on speed.
  • Mercurial – Mercuial is a cross-platform version control tool with a command line interface. Popular user interfaces exist such as TortoiseHg. Mercuial has an emphasis on high performance, scalability, advanced branching and merging, and robust handling of plain text and binary files.
  • SVN – SVN was designed as the successor to a once widely used CVS system. Related: TortoiseSVN.
  • Perforce – Perforce is a comemrcial revision control system. Is often integrated into the SDK’s of game engines such as Unreal and Unity. Has licensing for small teams.

 

Now how do you know which one to pick? There is no ‘best’ one – like most of game development you’ve got to just point your finger and pick one and stick with it until you find a reason to not. Choose something with a nice looking UI or other feature and give it a shot. If you’re unhappy with it, look at the Pros and Cons of the others and pick one of those and try again!

Note: Different VCS systems handle large binary files differently. This is something to be aware of when looking into VCS systems. Binary files can cause large bloat in repository size because a unique copy of a 10mb PSD file will be created each time it is checked in. With a text based asset only the changes are stored but most VCS systems are forced to store the entire binary file at each revision. There is not an easy way to permanently delete old copies of files from your version history. This is probably not an issue if you’re just getting started out, but something to consider before you check 3gb of assets into a repository! If you think this is going to be an issue for your project, look into the UDK Guide further down, the StackOverflow post has some information about large file handling.

Great, so now I’ve decided on Git/Mercurial/SVN/Perforce, where can I get a repository?
While each VCS software should come with server software that will let you set up your own server, there’s also some common hosts that will host your code for you.

  • BitBucket – Free for up to 5 users in a repository with unlimited private repos/public repos. This means that teams of 5 or less users can use a Bitbucket repo for free. After that they have a pricing plan that starts at $10/month and goes up. Public repos mean that the code is visible to anyone on the internet with every commit. A Private repo on the other hand is not visible. Both public and private repos will allow you to limit access to who can contribute to the project. Supports Mercurial and Git.
  • GitHub – Unlimited users. Unlimited public repositories, no private repositories at their free tier. Prices start at $7/mo and go upwards. Github is good for open source projects with lots of contributors while BitBucket may be better suited for small teams.
  • Gitorious – Provides free hosting for open-source projects that use Git.
  • GitLab – Allows self-hosting of repositories.

Can I use Version Control within Unity/UDK/Etc?
Unity – Guide to External Version Control – Don’t use Unity’s “Asset Server” addon, it’s terrible. Perforce support is supposedly also an available addon as of Unity 4.3.
UDK – I believe UDK has built in support for Perforce integration. External VCS can still be used. Overview of External Version Control.

Cons to not using VCS?
You will receive infinite ridicule if anyone ever finds out that you’re not using VCS.

What about other of ?
This is intended as an introductory guide to some of the common usages/benefits of VCS. There are much more powerful things you can do with VCS than is covered in this guide. These are topics for another guide or self-discovery.

I have been working on implementing Ads into LuxelOperation’s current project for the past week or so. Having finally wrapped up all of the loose ends I turned it over to one of my routine play testers. He immediately found what was not only a game breaking bug, but a show stopping bug and I didn’t have an easy answer (after looking at the log output) of why.

Our project has a UI overlay that closes two doors over the scene when we change scenes. This covers up the scene change and allows us to give an obvious loading screen if it’s going to be more than an instantaneous change. We decided to trigger the overlay to “close” the doors, and then display an ad, and after the user returns from watching the ad the doors would open back up. This gave some notification that an Ad was about to happen and really helps on older devices where the change in Android Activity’s is a sluggish and jarring process (something which is almost painless on modern devices fortunately).

The Problem:

What was happening in play tests was that the first time you began playing the game in a session, everything would be peachy. The ads would show up fine, the doors would open and close and everything went exactly as planned. However, if you returned to the main menu and then went back into the Gameplay scene as soon as the first ad finished playing the doors would never open and the game would be unplayable.

A short time looking through the logs later showed that the script that moved the UI components was suddenly throwing NullReferenceExceptions because it couldn’t find the doors… Which had worked fine up until we returned from the ad, and worked fine the first time the scene was loaded. This was very confusing because the doors are not persistent through scenes, so by reloading the Gameplay scene a second time it should have been exactly like the first time. (The doors do not use Unity’s DontDestroyOnLoad).

Many hours of printf-style debugging later, I finally came to the conclusion that the MonoBehaviour that was calling the shots was null. Except, the other MonoBehaviours it referenced weren’t null, but things they referenced were null! Confused and frustrated, I called it quits for the night only to have something tickle the back of my brain… C# Events.

As near as I can tell, the final score is this:

  1. GameObject “A” registered a delegate to an Event that takes place on GameObject “B” (which was marked as DontDestroyOnLoad (and thus was persistent)).
  2. The Scene was loaded, the Delegate was registered, and everything was fine.
  3. The Scene was unloaded, and everything was destroyed. Except the GameObject still had a reference to it the form of that Delegate (as far as the C# Garbage Collector is concerned), so the GameObject persisted in C# memory but not in the Unity Hierarchy.
  4. The Scene was reloaded a short time later and a new copy of it was made and a second event was registered.
  5. The callback was called when an ad finished and this event was broadcast to both copies of the GameObject, the real one (from the second scene load), and the ‘ghost’ one (which only persisted in C# memory).
  6. The ‘ghost’ GameObject tried to reference other GameObjects from the first time the scene was loaded which no longer existed and threw a NullReferenceException.

The Fix:

void OnDisable()
{
	LuxelAds.OnAdClosed -= OnAdFinishedCallback;
}

Yup, just one line.

Best Practices for C# Events in Unity:

When using Events on Unity GameObjects, they should register their callbacks in the OnEnable() function, and unregister their callbacks in the OnDisable() function. The reason we do this in OnEnable and OnDisable (instead of Awake() and Destroy()) is so that code does not get executed on an Inactive GameObject (which probably leads to more debugging nightmares).

Earlier today I had a situation where I wanted to be able to place an element outside of the users screen while using a UIAnchor set to Center.
The reason for this was that the element needed to line up with a duplicate element in the center of the screen without overlapping. The two elements in this example are two halves of a full screen overlay (a “panel”) designed to hide the scene for menu transitions.

the_goal

The easiest method to get the two UI panels to get to the center and line up pixel perfect is to set the Pivot point on the left panel to be on the right side, and on the right panel do the opposite, setting the pivot to be the left center. This meant that when the localPosition of both panels was set to 0,0,0 they would line up perfectly in the middle of the screen without overlapping and it’d work on all resolutions/aspect ratios. (A UI Stretch component was added that set their Relative Size’s X to .5, so they each take up half of the width. The UI Root in this case is set to FixedSize)

Now came other half of the equation: Ensuring that each of these panels would start off screen and non-visible to the player so they could smoothly close without starting halfway onscreen, etc. A simple bit of math tells us that if we take Unity’s Screen.width and divide that by two then we could simply set the localPosition of each panel to be that far and they’d show up off screen. However, upon testing this I noticed a problem: If the code was run on anything but the ‘native’ resolution (1280×800 in this case), the panels would either show up too far off screen, or still be onscreen!

whats_happening

The reason for this is that when NGUI uses the FixedSize and it scales everything up/down from the 800 pixel height, it also impacts the horizontal positioning of elements. Why this specifically happens is unclear, but the fix is easy enough.

Instead of:

int panelOffset = Screen.width/2;
LeftPanel.transform.localPosition = new Vector3(-panelOffset, 0, 0);
RightPanel.transform.localPosition = new Vector3(panelOffset, 0, 0);

We use this neat little function, UIRoot.GetPixelSizeAdjustment. While the docs are unfortunately fairly vague, it’s pretty simple to use in practice:

int paneloffset = Mathf.FloorToInt(UIRoot.GetPixelSizeAdjustment(LeftPanel)*(Screen.width/2f))+1;
LeftPanel.transform.localPosition = new Vector3(-paneloffset, 0, 0);
RightPanel.transform.localPosition = new Vector3(paneloffset, 0, 0);

The reason for the Mathf.FloorToInt(…)+1 is to ensure that the elements definitely stay off screen. Due to floating point calculations the panels will often fall on a half or third pixel on the inside of the screen. By flooring it and adding one we ensure that we stay on a whole pixel and we stay off screen.

Tada!