Hi,
I am trying to store custom build configuration files and execute them with one button. They can have varying scripting define symbols and platform targets.
According to the docs, I need to return the control back to the editor after updating the scripting define symbols and before building the player:
How can I do that?
The only solution I have found was to create a button that applies the settings, and another button that builds the player, so that the Editor definitely regains control after the first button press and can compile the scripts.
Generally, you can use EditorApplication.delayCall to get yourself out of situations where something is not allowed. E.g. register a delayedCall callback in compilationFinished and then trigger the build form there.
But changing the scripting defines also triggers a domain reload and any callbacks won’t survive that. You’ll need to store your state as something serializable and then continue when your editor scripts are reloaded after the domain reload.
Thank you for the suggestions.
I tried using BuildPlayerOptions.extraScriptingDefines, but it does not seem to apply the scripting defines.
I isolated the relevant code and created a new project using Unity 2021.3.14f1 with the following editor class:
using UnityEngine;
using UnityEditor;
using System.Linq;
public class BuildTest : MonoBehaviour
{
[MenuItem("Test/BuildClient")]
public static void BuildClient()
{
var path = "Builds/Client/Client.exe";
var scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray();
var bpOptions = new BuildPlayerOptions()
{
locationPathName = path,
targetGroup = BuildTargetGroup.Standalone,
subtarget = (int)StandaloneBuildSubtarget.Player,
target = BuildTarget.StandaloneWindows64,
options = BuildOptions.None,
extraScriptingDefines = new[] { "TEST_CLIENT" },
scenes = scenes
};
var definesText = string.Join(";", bpOptions.extraScriptingDefines);
Debug.Log($"Extra defines: {definesText}"); // prints: "Extra defines: TEST_CLIENT"
var report = BuildPipeline.BuildPlayer(scenes, path, bpOptions.target, bpOptions.options);
}
}
The resulting executable has a text UI that changes if TEST_CLIENT is set. In my case, it doesn’t, suggesting that TEST_CLIENT is not set.
Am I missing any steps?
For completeness, this is the class that changes the text depending on the scripting defines.
The text should show:
TEST_SERVER: false```
but shows:
```TEST_CLIENT: false
TEST_SERVER: false```
```csharp
using UnityEngine;
public class TestText : MonoBehaviour
{
private void Awake()
{
var sb = new System.Text.StringBuilder();
sb.Append("TEST_CLIENT: ");
#if TEST_CLIENT
sb.AppendLine("true");
#else
sb.AppendLine("false");
#endif
sb.Append("TEST_SERVER: ");
#if TEST_SERVER
sb.AppendLine("true");
#else
sb.AppendLine("false");
#endif
Debug.Log(sb);
if (TryGetComponent(out TMPro.TextMeshProUGUI textComponent))
{
textComponent.text = sb.ToString();
}
}
}
You’re not passing bpOtions to BuildPlayer(), only some parts of it. So the value of extraScriptingDefines never reaches Unity.
Instead, use the BuildPlayer(BuildPlayerOptions buildPlayerOptions) overload that directly accepts bpOtions and nothing else. The scenes and build path are already set on the options, no need to pass them as separate arguments.
Thank you for pointing that out! (I also fixed the typo in bpOptions)
The only remaining issue is that switching the subtarget (player ↔ server) will require the build button to be pressed two times, until UNITY_SERVER is set or unset correctly. But that seems to be the issue discribed in the bug I linked in the first post, so I will probably use the serialize-build-intent-workaround you described until this issue is fixed.
Edit:
Apparently, this is all I needed for my second issue. The subtarget can be set manually before calling EditorUserBuildSettings.SwitchActiveBuildTarget.
EditorUserBuildSettings.standaloneBuildSubtarget = StandaloneBuildSubtarget.Player;
EditorUserBuildSettings.SwitchActiveBuildTarget(bpOptions.targetGroup, bpOptions.target);
var report = BuildPipeline.BuildPlayer(bpOptions);
Apologies for reviving an old thread, but I’m currently facing an issue with using scripting define symbols in Unity. I’ve followed the guidance mentioned in this thread but failed to use scripting define symboles.
In my building function, as shown below, I set the extraScriptingDefines to "TEST_B":
public static void BuildAPK()
{
var buildPlayerOptions = new BuildPlayerOptions();
buildPlayerOptions.targetGroup = BuildTargetGroup.Android;
buildPlayerOptions.target = BuildTarget.Android;
buildPlayerOptions.locationPathName = "Release/Test.apk";
buildPlayerOptions.extraScriptingDefines = new string[] { "TEST_B" };
var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
if (report.summary.result == BuildResult.Succeeded)
{
Debug.Log("Build Success");
}
else if (report.summary.result == BuildResult.Failed)
{
Debug.LogError("Build Failed");
}
}
In my testing function (Awake), I have conditional compilation based on the define symbols:
private void Awake()
{
#if TEST_A
Debug.Log("This is TEST A.");
#elif TEST_B
Debug.Log("This is TEST B.");
#endif
}
However, even when extraScriptingDefines is set to "TEST_B", the log output always displays “This is TEST A”.
I was using Unity version 2021.3.8 and defined the symbols in Player Settings > Script Compilation.
The building function is called by the command line.
I’m wondering if there’s something I’m misunderstanding or if there’s an issue with my approach. Thank you in advance for any insights or suggestions you may have to help me resolve this matter.
If you do get “This is TEST A” in console, then TEST_A must be defined and overrides TEST_B. I suspect TEST_A is defined in your player settings?
As the name implies extraScriptingDefines are symbols defined in addition to those in player settings, they don’t override the existing ones (and it’s not possible to un-define a symbol this way).
If you change your Awake to not use elif, you should see if both A and B are defined:
private void Awake()
{
#if TEST_A
Debug.Log("This is TEST A.");
#endif
#if TEST_B
Debug.Log("This is TEST B.");
#endif
}
Thank you for your response!
I followed your suggestion and removed the symbols that were previously defined in Project Settings. Instead, I added them solely in extraScriptingDefines, and this resolved the issue. Thank you a lot.
However, I noticed that by adding the symbols only via script, I no longer have code suggestions or completion for those symbols.
Is there a way to maintain code suggestion functionality while still triggering the build only with certain symbols?
No, only the symbols set in Player Settings will be used in your IDE.
Whenever possible, I try to compile all code in the editor and only disable parts in the builds, i.e. using #if TEST_A || UNITY_EDITOR instead. You can’t use elif in this case and might have to do editor-only runtime checks but it will prevent compilation errors from suddenly showing up in builds.
You can also just do runtime checks instead of conditional compilation. In many instances that’s much simpler and the overhead of a few ifs here and there is negligible.