Skip to content

Instantly share code, notes, and snippets.

@instance-id
Last active October 3, 2022 18:32
Show Gist options
  • Save instance-id/3161cc2b5343db5bc3cef494d83a7449 to your computer and use it in GitHub Desktop.
Save instance-id/3161cc2b5343db5bc3cef494d83a7449 to your computer and use it in GitHub Desktop.
Launch unity project by name (automatically in correct editor version). Automatically fix Unity: "Failed to load layout window" issue. Create new project from CLI. Add packages / assets to project before launch
#!/usr/bin/env pwsh
<#
.NOTES
============================================================
Last Edit: 5/29/2021
Created by: instance.id (https://github.com/instance-id)
Platform: Windows/Linux
Filename: unitytool.ps1
PSVersion: Last tested with 7.2-preview5
Requirements: fd : https://github.com/sharkdp/fd
============================================================
.DESCRIPTION
Unity multi tool, launcher, version upgrade, package/asset installer, default layout fix
.EXAMPLE
.\unitytool.ps1 "MyProjectName" -Fix -Run
.\unitytool.ps1 "MyProjectName" -fr -s
.\unitytool.ps1 . -s
.PARAMETER Project
The name of the project/root folder
.PARAMETER Path
The Path to the project root folder
.PARAMETER Fix
Automatically fix the "Failed to load layout window" issue
.PARAMETER Run
Start the project up after fixing the issue if included, or will simply open the project if fix is not needed.
.PARAMETER FRun
Both -Fix and -Run combined for ease of use
.PARAMETER Open
Open the folder in the file explorer
.PARAMETER Setup
Add custom packages and assets to the project before starting it
.PARAMETER Create
Create a new Unity project, must include Project as name and Path for location
.PARAMETER Backup
Execute the specified backup script and pass it the project name and location
.PARAMETER SetVersion
Open project with a version that is different than the currently specified version
#>
Param (
[Parameter(Position = 0)]
[string]$Project,
[Alias('p')][string]$Path,
[Alias('f')][switch]$Fix,
[Alias('r')][switch]$Run,
[Alias('o')][switch]$Open,
[Alias('s')][switch]$Setup,
[Alias('fr')][switch]$FRun,
[Alias('b')][switch]$Backup,
[Alias('c')][switch]$Create,
[Alias('v')][string]$SetVersion
)
# Checking to make sure project name and Unity version are included
if ($Project) { Write-Host "Locating ${Project}..." }
else { Write-Host 'Unity project name required...'; exit }
# -- Possibly needed for starting batch mode, so I have heard.
# -- I didn't find that I needed it, though
$licenseFile = '/.local/share/unity3d/Unity/Unity_lic.ulf'
# -------------------------------------- Packages
# -- Either create an environmental variable named $PACKAGES
# -- which points to the location you store your custom packages
# -- or change $packages below to the hardcoded path
# $packages = "/path/to/custom/packages"
$assets = $env:ASSETS
$packages = $env:PACKAGES
<#
Example package setup:
$packageDict = [PSCustomObject]@{
'com.domain.githubpackage1' = "https://github.com/com.domain.package2/MyPackage1"
'com.domain.localpackage2' = "file:${packages}/com.domain.package2/Assets/instance.id/Package2"
'com.domain.package3' = "file:${packages}/com.domain.package2/Assets/instance.id/Package3"
}
#>
$packageDict = [PSCustomObject]@{ 'id.instance.projectinit' = "file:${packages}/id.instance.projectinit/Assets/instance.id/ProjectInit" }
# ------------------------------- System Specific
$projectBackupScript = ''
if ($IsWindows) {
$driveDir = 'E:'
$projectBackupScript = "$HOME/.backup/_projects/projects_backup.ps1"
$hubLocation = 'C:/Program Files/Unity/Hub/Editor'
$unity = 'Unity.exe'
}
if ($IsLinux) {
$driveDir = '/mnt/x'
$projectBackupScript = "$HOME/.backup/_projects/projects_backup.ps1"
$hubLocation = "$HOME/Unity/Hub/Editor"
$unity = 'Unity'
}
$projectFound = $false
$packageResult = $false
# ------------------------------------------------------------------------------------------
# Input the root folder location that contain your Unity projects --------------------------
# Ex: "C:/Unity/MyProjects" : If located in multiple folders,
# enter the parent which contains all of them. This now uses fd to search, and it is
# extremely fast.
# (Prior search, which took 3-4 minutes on a Sabrent 4.0 Rocket NVME, fd takes 4-5 seconds)
# -- Default Unnity project search root
$location = '/mnt/x/'
# -- Default is overridden by passing $Path from CLI
if (!([string]::IsNullOrEmpty($Path))) { $location = $Path }
# -- If you plan to also use my backup script, put it's path here
$projectBackupScript = "$HOME/.backup/_projects/projects_backup.ps1"
# -- Needed variables for file names and locations
# ------------------------------ Layout
$originalLayout = 'Default.wlt'
$destinationLayout = 'CurrentLayout-default.dwlt'
$destinationLayoutPath = "/Library/${destinationLayout}"
$editorLocation = ''
# ---------------------------- Location
$folderName = ''
$projectPath = ''
$destinationPath = ''
# --------------------- Project Version
$versionNumber = ''
$projectVersionFile = ''
$projectVersionTxt = 'ProjectVersion.txt'
$editorVersions = [System.Collections.ArrayList]::new()
# --------------------------------------------- Helper Functions
# --------------------------------------------------------------
# ----------------------------- Select project version
function SelectVersion {
$editorPaths = (get-item $hubLocation).FullName | gci -Directory
$editorPaths | % { $versionNum = (get-item $_).BaseName; $null = $editorVersions.Add($versionNum) }
$editorVersions.Reverse()
$index = 0
$indexVal = ''
write-host "Select Version of Unity for Project: ${Project}"
foreach ($item in $editorVersions) {
if ($index -lt 10) { $indexVal = "[0${index}]" }
else { $indexVal = "[${index}]" }
write-host "${indexVal} ${item}"
$index++
}
[int]$result = Read-Host 'Enter integer value from above'
return $result
}
# ------------------------ Get project current version
function GetVersion {
$versionSearch = ‘*m_EditorVersion: *’
$file_data = Get-Content $projectVersionFile | Where-Object { $_ -like "${versionSearch}"; }
$separator = ': '
$versionString = $file_data.split($separator);
$version = ${versionString}[1]
return $version
}
# ----------------------------- Add packages to project manifest
# --------------------------------------------------------------
function AddPackages {
# ----------------- Copy Standard Assets folder into project
$assetSource = [System.IO.Path]::Combine($assets, 'Standard Assets', 'Essentials')
$assetPath = [System.IO.Path]::Combine($projectPath, 'Assets')
if (Test-Path $assetSource) {
if (!(Test-Path $([System.IO.Path]::Combine($projectPath, 'Assets', 'Standard Assets', 'Essentials')))) {
Copy-item -Force -Recurse -Path $assetSource -Destination $assetPath
} else { write-host 'Essential assets already exist in project' }
}
# --------- Edit the project manifest to add custom packages
$manifest = [System.IO.Path]::Combine($projectPath, 'Packages', 'manifest.json')
if (!(Test-Path $manifest)) { Write-Host 'Could not locate project manifest.json. Skipping package injection' -ForegroundColor Red; return }
$json = Get-Content -Encoding UTF8 $manifest | ConvertFrom-Json
$packageDict.PSObject.Properties | % {
if (!($json.dependencies | Get-Member -Type NoteProperty -Name "$($_.Name)")) {
write-host "Adding Package $($_.Name) to manifest.json"
$json.dependencies | Add-Member -Type NoteProperty -Name "$($_.Name)" -Value "$($_.Value)" -ErrorAction SilentlyContinuepower
$packageResult = $true
} else { write-host "Package $($_.Name) already exists" }
}
if ($packageResult) { $json | ConvertTo-Json | Set-Content -Encoding UTF8 $manifest }
}
function WriteError($type, $path, $folder) { Write-Host "Error creating folder: ${folder} in ${path}"; exit }
# -------------------------------------------- Setup new project
# --------------------------------------------------------------
function CreateProjectStructure($projPath, $version) {
$as = 'Assets'
$ps = 'ProjectSettings'
$pv = $projectVersionTxt
$pSettingsTxt = @"
m_EditorVersion: ${version}
m_EditorVersionWithRevision: ${version}
"@
$projectPath = [System.IO.Path]::Combine($projPath, $Project)
$pPath = [System.IO.Path]::Combine($projectPath, 'ProjectSettings')
if (!(Test-Path $projectPath)) { New-Item -Path $projPath -ItemType Directory -Name $Project -Force }
$fPath = get-item $projectPath
if (Test-Path $fPath) { New-Item -Path $fPath -ItemType Directory -Name $as } else { WriteError 'Folder' $fPath $as }
if (Test-Path $fPath) { New-Item -Path $fPath -ItemType Directory -Name $ps } else { WriteError 'Folder' $fPath $ps }
if (Test-Path $pPath) { New-Item -Path $pPath -ItemType File -Name $pv -Value $pSettingsTxt } else { WriteError 'File' $pPath $pv }
write-host 'Project structure created..'
return $fPath
}
function CreateProject {
if ($([string]::IsNullOrEmpty($SetVersion))) {
$result = SelectVersion
$versionNumber = $editorVersions[$result]
} else { $versionNumber = $SetVersion }
write-host "Editor Version ${versionNumber}"
$editorLocation = "$hubLocation/$versionNumber/Editor/$unity"
if (!(Test-Path $editorLocation)) { Write-Host "Unity Editor version: ${versionNumber} not found. Please download or open in another version" -ForegroundColor Red; exit }
if ( [String]::IsNullOrEmpty($Path)) { $Path = Read-Host 'Path for new project?' }
$projectPath = CreateProjectStructure $Path $versionNumber
if ($Setup.IsPresent) {
write-host 'Creating initial project files'
& $editorLocation -projectPath $projectPath -Quit -BatchMode -manualLicenseFile $licenseFile
write-host "Adding custom packages to ${Project}"; AddPackages
& $editorLocation -projectPath $projectPath -Quit -BatchMode -manualLicenseFile $licenseFile
& $editorLocation -projectPath $projectPath
} else { & $editorLocation -projectPath $projectPath }
exit
}
# --------------------------------- Process the project requests
function ProcessProject {
Write-Host "Unity project $Project found: $projectPath"
if ($([string]::IsNullOrEmpty($SetVersion))) {
$versionNumber = GetVersion
$editorLocation = [System.IO.Path]::Combine($hubLocation, $versionNumber, 'Editor', $unity)
} else {
$versionNumber = $SetVersion
$editorLocation = [System.IO.Path]::Combine($hubLocation, $SetVersion, 'Editor', $unity)
Write-Host "Updating project editor version to ${setversion}. Using: ${editorLocation}"
}
# ------------------------------------------ Fix Layout file
if ($FRun -or $Fix) {
$destinationPath = [System.IO.Path]::Combine(${projectPath}, ${destinationLayoutPath})
$originalLayoutFile = "$hubLocation/$versionNumber/Editor/Data/Resources/Layouts/$originalLayout"
if (Test-Path $originalLayoutFile) {
Write-Host "Original layout file for Unity version $versionNumber found..."
if ( [System.IO.File]::Exists($destinationPath)) {
Write-Host "Probematic layout file found: $destinationPath"
# Backup original layout file
Write-Host "Creating backup of original layout file at: ${destinationPath}.bak"
Copy-item -Force -Verbose -Path ${destinationPath} -Destination "${destinationPath}.bak"
# Copy Default layout for editor version from install folder and rename to required filename
Write-Host "Copying default layout for version $versionNumber to $Project Library..."
Copy-item -Force -Verbose -Path $originalLayoutFile -Destination "${destinationPath}"
} else {
Write-Host "Could not locate probematic layout file at location: $destinationPath" -ForegroundColor Red
Write-Host "Please check that Library folder exists at path: ${projectPath}/Library" -ForegroundColor Red
}
} else {
Write-Host "Unity $versionNumber not found" -ForegroundColor Red; return;
}
Write-Host 'Layout fix completed' -ForegroundColor Green
}
# --------------------------- Install any packages or assets
if ($Setup.IsPresent) { write-host "Adding custom packages to ${Project}"; AddPackages }
# -------------------------------------------- Start Project
if ($FRun.IsPresent -or $Run.IsPresent) {
if (Test-Path $editorLocation) {
Write-Host "Starting Project: ${Project}" -ForegroundColor Green
if ($packageResult) {
# -- Quickly opens the project in batch mode and automatically close it so that the newly added packages
# -- can be added, then opens it again normally, so that it will be ready to use
& $editorLocation -projectPath $projectPath -Quit -BatchMode <# -manualLicenseFile $licenseFile #>
& $editorLocation -projectPath $projectPath <# -manualLicenseFile $licenseFile #>
} else {
& $editorLocation -projectPath $projectPath <# -manualLicenseFile $licenseFile #>
}
} else { Write-Host "Unity Editor version: ${versionNumber} not found. Please download or open in another version" -ForegroundColor Red }
}
# ------------------------------------ Open Project Location
if ($Open.IsPresent) {
Write-Host "Opening location: ${foundProjectLocation}"
if ($IsWindows) { & explorer.exe $projectPath } else { & nemo $projectPath }
}
# ------------------------------------------- Backup project
if ($Backup.IsPresent) {
Write-Host "Backing up project ${Project}: ${foundProjectLocation}"
& $projectBackupScript -Project $Project -Source $projectPath
} else { Write-Host "Project: ${Project} Editor: ${versionNumber} Location: ${foundProjectLocation}" }
}
# -------------------------------------------- Program Execution
# --------------------------------------------------------------
if ($Create) {
CreateProject
}
if ($Project -eq '.') {
$projectPath = [System.IO.Directory]::GetCurrentDirectory()
$projectName = (get-item $projectPath).Directory.BaseName
$projectVersionFile = [System.IO.Path]::Combine(${projectPath}, 'ProjectSettings', $projectVersionTxt)
if (!(Test-Path $projectVersionFile)) { Write-Host 'Could not find project version file' -ForegroundColor Red; exit }
if ($([string]::IsNullOrEmpty($SetVersion))) { $versionNumber = GetVersion } else { $versionNumber = $SetVersion }
$editorLocation = "$hubLocation/$versionNumber/Editor/$unity"
if (!(Test-Path $editorLocation)) {
Write-Host "Unity Editor version: ${versionNumber} not found. Please download or open in another version" -ForegroundColor Red; exit
}
if ($Setup.IsPresent) {
# -- Add custom packages and assets to project and run project
write-host "Adding custom packages to ${projectName}"; AddPackages
# -- Determine if Unity is already opened with this project
$Programs = 'Unity'
$Running = Get-Process -Name $Programs -ErrorAction SilentlyContinue
# -- If project is already running, exit script
$exists = $false
foreach ($item in $Running.CommandLine) {
write-host $item
if ( $item.ToString().EndsWith($Project)) {
$exists = $true
write-host "${Project} already running"
exit
}
}
}
if ($packageResult) {
# -- Quickly opens the project in batch mode and automatically close it so that the newly added packages
# -- can be added, then opens it again normally, so that it will be ready to use
& $editorLocation -projectPath $projectPath -Quit -BatchMode <# -manualLicenseFile $licenseFile #>
& $editorLocation -projectPath $projectPath <# -manualLicenseFile $licenseFile #>
} else {
& $editorLocation -projectPath $projectPath <# -manualLicenseFile $licenseFile #>
}
exit
} else {
# -- Using fd's extremely fast search, look for ProjectVersion.txt file in the location specified
[String[]]$results = & fd "${projectVersionTxt}" "${location}" --xdev --follow --hidden
# -- Iterate all found files and determine if they are Unity projects, and which is the one we requested
for ($i = 0; $i -lt $results.Count; $i++) {
$projectVersionFile = $results[$i]
$projectPath = (get-item $projectVersionFile).Directory.Parent
$folderName = (get-item $projectPath).BaseName
if ($folderName -eq $Project) { $projectFound = $true; } else { continue; }
# -- If the correct project has been found, pass it to the ProcessProject method
if ($projectFound) { ProcessProject } else { Write-Host "${Project} not found, or it is not a project folder." -ForegroundColor Red; exit; }
}
}
@bloodfor1
Copy link

When I upgrade, I encounter many errors in the project.
I will try this now

@bloodfor1
Copy link

bro i need to update the version. We tried everything, it doesn't work, thank you very much

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