Skip to content

Instantly share code, notes, and snippets.

@adammyhre
Last active September 1, 2024 13:05
Show Gist options
  • Save adammyhre/ce4009edccc420a35237419b5ea050e1 to your computer and use it in GitHub Desktop.
Save adammyhre/ce4009edccc420a35237419b5ea050e1 to your computer and use it in GitHub Desktop.
Automated Unity Project Setup Script
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
using static System.Environment;
using static System.IO.Path;
using static UnityEditor.AssetDatabase;
public static class ProjectSetup {
[MenuItem("Tools/Setup/Import Essential Assets")]
public static void ImportEssentials() {
Assets.ImportAsset("Odin Inspector and Serializer.unitypackage", "Sirenix/Editor ExtensionsSystem");
Assets.ImportAsset("Odin Validator.unitypackage", "Sirenix/Editor ExtensionsUtilities");
Assets.ImportAsset("Editor Console Pro.unitypackage", "FlyingWorm/Editor ExtensionsSystem");
// and so on...
}
[MenuItem("Tools/Setup/Install Essential Packages")]
public static void InstallPackages() {
Packages.InstallPackages(new[] {
"com.unity.2d.animation",
"git+https://github.com/adammyhre/Unity-Utils.git",
"git+https://github.com/adammyhre/Unity-Improved-Timers.git",
"git+https://github.com/KyleBanks/scene-ref-attribute.git"
// If necessary, import new Input System last as it requires a Unity Editor restart
// "com.unity.inputsystem"
});
}
[MenuItem("Tools/Setup/Create Folders")]
public static void CreateFolders() {
Folders.Create("_Project", "Animation", "Art", "Materials", "Prefabs", "Scripts/Tests", "Scripts/Tests/Editor", "Scripts/Tests/Runtime");
Refresh();
Folders.Move("_Project", "Scenes");
Folders.Move("_Project", "Settings");
Folders.Delete("TutorialInfo");
Refresh();
MoveAsset("Assets/InputSystem_Actions.inputactions", "Assets/_Project/Settings/InputSystem_Actions.inputactions");
DeleteAsset("Assets/Readme.asset");
Refresh();
// Optional: Disable Domain Reload
// EditorSettings.enterPlayModeOptions = EnterPlayModeOptions.DisableDomainReload | EnterPlayModeOptions.DisableSceneReload;
}
static class Assets {
public static void ImportAsset(string asset, string folder) {
string basePath;
if (OSVersion.Platform is PlatformID.MacOSX or PlatformID.Unix) {
string homeDirectory = GetFolderPath(SpecialFolder.Personal);
basePath = Combine(homeDirectory, "Library/Unity/Asset Store-5.x");
} else {
string defaultPath = Combine(GetFolderPath(SpecialFolder.ApplicationData), "Unity");
basePath = Combine(EditorPrefs.GetString("AssetStoreCacheRootPath", defaultPath), "Asset Store-5.x");
}
asset = asset.EndsWith(".unitypackage") ? asset : asset + ".unitypackage";
string fullPath = Combine(basePath, folder, asset);
if (!File.Exists(fullPath)) {
throw new FileNotFoundException($"The asset package was not found at the path: {fullPath}");
}
ImportPackage(fullPath, false);
}
}
static class Packages {
static AddRequest request;
static Queue<string> packagesToInstall = new Queue<string>();
public static void InstallPackages(string[] packages) {
foreach (var package in packages) {
packagesToInstall.Enqueue(package);
}
if (packagesToInstall.Count > 0) {
StartNextPackageInstallation();
}
}
static async void StartNextPackageInstallation() {
request = Client.Add(packagesToInstall.Dequeue());
while (!request.IsCompleted) await Task.Delay(10);
if (request.Status == StatusCode.Success) Debug.Log("Installed: " + request.Result.packageId);
else if (request.Status >= StatusCode.Failure) Debug.LogError(request.Error.message);
if (packagesToInstall.Count > 0) {
await Task.Delay(1000);
StartNextPackageInstallation();
}
}
}
static class Folders {
public static void Create(string root, params string[] folders) {
var fullpath = Combine(Application.dataPath, root);
if (!Directory.Exists(fullpath)) {
Directory.CreateDirectory(fullpath);
}
foreach (var folder in folders) {
CreateSubFolders(fullpath, folder);
}
}
static void CreateSubFolders(string rootPath, string folderHierarchy) {
var folders = folderHierarchy.Split('/');
var currentPath = rootPath;
foreach (var folder in folders) {
currentPath = Combine(currentPath, folder);
if (!Directory.Exists(currentPath)) {
Directory.CreateDirectory(currentPath);
}
}
}
public static void Move(string newParent, string folderName) {
var sourcePath = $"Assets/{folderName}";
if (IsValidFolder(sourcePath)) {
var destinationPath = $"Assets/{newParent}/{folderName}";
var error = MoveAsset(sourcePath, destinationPath);
if (!string.IsNullOrEmpty(error)) {
Debug.LogError($"Failed to move {folderName}: {error}");
}
}
}
public static void Delete(string folderName) {
var pathToDelete = $"Assets/{folderName}";
if (IsValidFolder(pathToDelete)) {
DeleteAsset(pathToDelete);
}
}
}
}
@KolanThal
Copy link

The AssetStore cache path can be changed in Unity preferences. The modified path can be fetched by e.g.

            public static void ImportAsset(string asset, string folder)
            {
                var defaultPath = Combine(GetFolderPath(SpecialFolder.ApplicationData), "Unity"); 
                var assetsFolder = Combine(EditorPrefs.GetString("AssetStoreCacheRootPath", defaultPath), "Asset Store-5.x");               
                asset = asset.EndsWith(".unitypackage") ? asset : asset + ".unitypackage";
                ImportPackage(Combine(assetsFolder, folder, asset), false);
            }

Only tested with Unity 2022.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment