Simplify build builds in Unity3D

Most recently, I realized that with the increasing number of finished projects, more and more time has to be devoted to building builds. It cannot be said that the unit somehow complicates this process, but certainly does not simplify it. Especially when each project is built for several platforms and even in different configurations. In principle, the problem is not new and has many different solutions. But for several reasons I stopped writing my own plugin.


It looks like this:



And why, why, where to get and how to use, I will tell below.


Pain


The first discomfort is usually felt when more than one platform is being prepared for release. It turns out that in the unit some project settings are common for all platforms, and some are not. And it must always be remembered and taken into account. But in general, you can live.


Then it turns out that there is no possibility to save different settings within the same platform. A simple example: build one, and there are many sites on which it will be distributed. And for each you need something different. Partially this problem is solved by removing the settings in the runtime configuration in StreamingAssets. But only partially, because only what is happening inside the game can be configured this way. The icon in this way can not be replaced. Icon each time you need to change yourself. Whether by code, by hand, but by hand. And at some point, the responsible manager, whom you trained for a month to work with StreamingAssets, goes on vacation, and builds are needed yesterday.


Later you will learn that a separate Korean version is planned, everything should be different there and without #ifdef.


And then you are told that the game needs to be launched on mobile platforms, playstation, nintendo and washing machines.


As a result, the preparation of each new build over time becomes similar to this:



Check the instrument readings, turn on the toggle switch on the left, turn the lever on the right, turn the red valve on. Do not confuse and do not forget.


At first, it can still and nothing, but quickly tires. And remember all the manipulations in a month or two.


What are your options?


Internets and folk wisdom offer us the following:


  • Solve a problem at the project level. The implementation of this approach can be different: separate projects with a common code and assets, submodules in a gita, branches in a gita, and the like.
    I can not seriously consider this. To fuss for the sake of replacing the icons, and then also to support. Quite an amateur. But I saw it with my own eyes, so I could not fail to mention it.
  • Bash / bat / python / ... scripts.
    Already warmer. But it requires additional knowledge and can hardly be called cross-platform (often a part of the team develops under Mac OS, some under Windows). Again, the scripts need to be supported, and there is not always time for this.
  • Editor script in the unit itself.
    Quite warm. Gate the project inside the unit as we want. And if you use the static method and the BuildPipeline, then you can customize the CI. As a rule, articles about units and CI are similar and described. However, there remains one drawback of the previous approach: this is the code and it should be written, append and maintain. We need something similar, but without the need for each platform and configuration to climb into the code.

Solved. We write our plugin with UI and fairies.


We need a plan


First you need to decide what we actually want to get and why.


  1. Storage of project settings for each configuration (here you can go to the terminology of the plugin itself and call them options). It is desirable to implement the inheritance options. For example, for all standalone builds, the project settings will be common, and the options from each other will differ only in the icon and the name of the project. It is quite logical to solve this by inheritance, not by copy-paste.
  2. A visual representation of what varied in a particular version and how it differs from the current project settings.
  3. Combining options in the collection. In order to be able to collect a subset of builds at once with one button or one static method.
  4. Packaging build archive. This is an optional requirement. In the case of using the plugin in a pair with the build server, this item can be implemented on its side. But to simplify life I want to simplify everything, but the build server is not always there.
  5. Moving files at different stages of assembly. For example, you need to replace any asset (icon, splash, scene, etc.) before building (replacing assets should be reversible, the build of the build should not change the current state of the project). Or rename it in the finished Windows {exe_name} _Data build to Data (a very annoying unit behavior, you can't rename the file without this manipulation). Or, again, in the Windows builds, arrange the necessary StreamingAssets. This should be done before the archive is packaged. And then for example drag all the archives somewhere in one place.
  6. Unity projects have a huge amount of settings and we absolutely do not want to write our own UI for all of them. Especially since he already is in the unit itself. Therefore, the project settings will change as usual, and in the plugin just follow the changes in settings and save to the desired options.
  7. I would like to store each option in a separate file so that I can carry them between projects.
  8. It is desirable to work with the unit since version 5.6. There are a number of legacy projects with a lot of fuss around builds.

Drove off


The main part of our plugin is to work with the project settings, as well as the storage and processing of individual options settings. Therefore, we focus on this.


All unit settings are stored in the ProjectSettings directory in the project root. First of all, during initialization, we will save these settings in the plugin directory (by the way, this is BuildVariants at the root of the project). This is required so that we can track all subsequent changes. Next we need to somehow store them. The simplest thing would be to simply copy ProjectSettings to a separate place for each option. And at the time of its activation just replace them entirely. The idea is quite working and at first I looked at it. But this completely prevents us from inheriting options. And if we are already writing a plugin, we will immediately make it beautiful. So we need to learn how to read and write the contents of ProjectSettings.


For this you need to understand what is stored there. And the most ordinary assets are stored there, which are serialized and deserialized in a regular way. This means that we can read them through AssetsDatabase and get a SerializedObject. This is already something. Because we can go through all the SerializedProperty, track their differences with the original "snapshot" and save them in the version. Then, when assembling or activating a variant, we collect changes across the entire inheritance chain, apply them to the "snapshot" and drop them in ProjectSettings. Voila


But SerializedProperty has some unpleasant features: the way of storing values ​​(all these intValue, floatValue, isArray), the lack of a normal mechanism for comparing them (there are two related objects, how do we understand whether they all have the same values ​​SerializedProperty) and paradoxical (and no) the absence of the Serializable attribute (which means we will need some kind of wrapper to save). I can not say that these are some fatal flaws, All this was done more than once in numerous editor scripts. But I still want something more simple, understandable and universal.


Or maybe we will slightly limit the use of our plugin? Let it work only on projects with textual serialization of assets (and someone else uses binary?). Then all ProjectSettings will be stored as yaml documents. You can take the YamlDotNet library and use it to parse and save the settings. At the same time and keep your configs in yaml. So even clearer. It is only necessary to add a few extensions for diffs yaml documents and their combination.


A little mess with the editor window (I don’t even want to describe it, I hope that with UIElements it will be easier and more fun to live), gathering everything in a heap and ready.


Result


It's time to show what I did . You have already familiarized with the main mechanisms of the plug-in operation, therefore, further on some features:


  • Variants are added to the selected collection using a check mark to the left of the name.
  • Build path must be specified with the extension. I did not find a complete list of extensions for all possible BuildTarget, so I left it for now.
  • Moving files is inherited in the same way as settings.
  • Not implemented diff arrays. Therefore, if in the inherited version you changed something in the list of scenes, then the entire list will be replaced entirely. For good it will be necessary to refine.
  • Actual project settings diff is a list of all the differences of the selected option from the current project settings. Moreover, if the active option is selected, then changes in the current settings can be undone.
  • Variant settings are respectively individual changes in the selected option, without regard to parental changes.
  • When you activate the option, all unsaved changes in the project settings will be lost.
  • If there are unsaved changes in the project settings, then any attempt to build a build through the plugin will end with an exception. Because see above, but as we remember "build build should not change the current state of the project."
  • The plugin can be used with the build server. To do this, there are methods of statics: BuildVariants.Controller.BuildController.BuildAll, BuildVariants.Controller.BuildController.BuildCollection (with the name of the collection in the -collection parameter) and BuildVariants.Controller.BuildController.BuildVariant (named variant in -variant parameter). It will look something like this:
    $unity_path -batchmode -projectPath $project_path -quit -logfile -executeMethod BuildVariants.Controller.BuildController.BuildCollection -collection standalone
  • For archiving, the plugin has another dependency in the form of the DotNetZip library.
  • The plugin was written in the conditions of a small working lull, so there has not yet been an opportunity to test and temper it in fierce battles. There may be bugs, perhaps even bad and unpleasant.

Future plans


Of course, I want to feed the plugin with features to infinity, but it's time to return to real projects, and at the same time test the existing functionality. A little bit later I want to apply UIElements for newer versions of unit, I hope this will make everything more beautiful and comfortable. I would also like to tie in versioning and some semblance of environment variables. Maybe the public will tell you something else in terms of functionality or usability.


Therefore, I will be glad to any wishes, comments, questions and bug reports. Easy builds to you!


Also popular now: