├── .gitignore ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Editor.meta ├── Editor ├── BuildHistory.meta └── BuildHistory │ ├── BuildData.cs │ ├── BuildData.cs.meta │ ├── BuildEntry.cs │ ├── BuildEntry.cs.meta │ ├── BuildHistoryEditor.cs │ ├── BuildHistoryEditor.cs.meta │ ├── BuildHook.cs │ ├── BuildHook.cs.meta │ ├── BuildPlatform.cs │ ├── BuildPlatform.cs.meta │ ├── EditorHelper.cs │ ├── EditorHelper.cs.meta │ ├── FronkonGames.Tools.BuildHistory.Editor.asmdef │ ├── FronkonGames.Tools.BuildHistory.Editor.asmdef.meta │ ├── LineChart.cs │ └── LineChart.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── Media.meta ├── Media ├── banner.png ├── banner.png.meta ├── howto.jpg ├── howto.jpg.meta ├── inspector_0.png ├── inspector_0.png.meta ├── inspector_1.png └── inspector_1.png.meta ├── README.md ├── README.md.meta ├── SampleScene.unity ├── SampleScene.unity.meta ├── package.json └── package.json.meta /.gitignore: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Unity. 3 | ############################### 4 | [Ll]ibrary/ 5 | [Tt]emp/ 6 | [Oo]bj/ 7 | [Ll]ogs/ 8 | [Ll]ibrary/ 9 | [Bb]uilds/ 10 | UnityGenerated/ 11 | UnityPackageManager/ 12 | ProBuilderPreferences.* 13 | [Rr]ecordings/ 14 | sysinfo.txt 15 | Assets/AssetStoreTools* 16 | *.pidb.meta 17 | *.pdb.meta 18 | *.stackdump 19 | project.json 20 | project.lock.json 21 | package-lock.json 22 | 23 | ############################### 24 | # Visual Studio. 25 | ############################### 26 | [Ee]xported[Oo]bj/ 27 | .vs/ 28 | *.userprefs 29 | *.csproj 30 | *.pidb 31 | *.suo 32 | *.sln* 33 | *.user 34 | *.unityproj 35 | *.booproj 36 | 37 | ############################### 38 | # Visual Studio Code. 39 | ############################### 40 | .vscode 41 | 42 | ############################### 43 | # JetBrains Rider. 44 | ############################### 45 | .idea/ 46 | /.idea 47 | JetBrains/ 48 | Unity/.idea/ 49 | JetBrains.* 50 | 51 | ############################### 52 | # DocFX. 53 | ############################### 54 | .cache 55 | /**/_site/ 56 | Documentation/api/ 57 | Documentation/index.* 58 | 59 | ############################### 60 | # OS. 61 | ############################### 62 | .DS_Store* 63 | ._* 64 | .Spotlight-V100 65 | .Trashes 66 | Icon? 67 | ehthumbs.db 68 | [Tt]humbs.db 69 | [Dd]esktop.ini 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## 1.0.1 - 2023-12-10 9 | 10 | ### Fixed 11 | - Fix build on Unity 2022 (thanks to @baba-s). 12 | - Small fixes in package.json. 13 | 14 | ## 1.0.0 - 2023-11-19 15 | 16 | _Initial release._ -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de1c021b3e0f2874f8b2dfc214506ac8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 52735f96e4d094f4995049701285ec55 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/BuildHistory.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba06b6124ec4bf64c93be49e7869e623 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildData.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using System.Collections.Generic; 18 | using UnityEditor; 19 | using UnityEditor.Build.Reporting; 20 | using UnityEngine; 21 | 22 | namespace FronkonGames.Tools.BuildHistory 23 | { 24 | /// Data storage. 25 | public static class BuildData 26 | { 27 | private const string BuildPrefix = "BuildEntry_"; 28 | 29 | private const int MaxEntries = 1000; 30 | 31 | /// Returns all the information of the builds. 32 | public static List Load() 33 | { 34 | List buildHistory = new(); 35 | 36 | for (int i = 0; i < MaxEntries; ++i) 37 | { 38 | string buildValue = EditorPrefs.GetString(GetKeyName(i)); 39 | if (string.IsNullOrEmpty(buildValue) == false) 40 | buildHistory.Add(new BuildEntry(buildValue)); 41 | } 42 | 43 | return buildHistory; 44 | } 45 | 46 | /// Store all build information. 47 | public static void Save(BuildReport buildReport) 48 | { 49 | if (buildReport != null) 50 | { 51 | int buildIndex = 0; 52 | while (EditorPrefs.GetString(GetKeyName(buildIndex)) != string.Empty) 53 | buildIndex++; 54 | 55 | if (buildIndex < MaxEntries) 56 | EditorPrefs.SetString(GetKeyName(buildIndex), new BuildEntry(buildReport).ToString()); 57 | else 58 | Debug.LogError("[FrokonGames.BuildHistory] Maximum number of builds exceeded."); 59 | } 60 | } 61 | 62 | /// Store build information. 63 | public static void Save(BuildEntry buildEntry) 64 | { 65 | if (buildEntry != null) 66 | EditorPrefs.SetString(buildEntry.name, buildEntry.ToString()); 67 | } 68 | 69 | /// Delete key. 70 | public static void Delete(string key) => EditorPrefs.DeleteKey(key); 71 | 72 | /// Delete key. 73 | public static void Delete(int index) => EditorPrefs.DeleteKey(GetKeyName(index)); 74 | 75 | /// Delete all keys. 76 | public static void DeleteAll() 77 | { 78 | for (int i = 0; i < MaxEntries; ++i) 79 | EditorPrefs.DeleteKey(GetKeyName(i)); 80 | } 81 | 82 | /// First free index to create a new entry. 83 | public static int FirstFreeIndex() 84 | { 85 | int buildIndex = 0; 86 | while (EditorPrefs.GetString(GetKeyName(buildIndex)) != string.Empty) 87 | buildIndex++; 88 | 89 | if (buildIndex >= MaxEntries) 90 | buildIndex = -1; 91 | 92 | return buildIndex; 93 | } 94 | 95 | /// Name of an entry. 96 | public static string GetKeyName(int index) => $"{BuildPrefix}{index:000}"; 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: abd16d5c08137a4478091e0ad08721c6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildEntry.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using System; 18 | using UnityEditor; 19 | using UnityEditor.Build.Reporting; 20 | 21 | namespace FronkonGames.Tools.BuildHistory 22 | { 23 | /// Information of a build. 24 | public class BuildEntry 25 | { 26 | /// Internal name. 27 | public string name; 28 | 29 | /// Date it started. 30 | public DateTime buildStartedAt; 31 | 32 | /// Platform selected for the build. 33 | public BuildTarget platform; 34 | 35 | /// Number of files processed in the build. 36 | public int totalFiles; 37 | 38 | /// Size in bytes of the build. 39 | public ulong totalSize; 40 | 41 | /// Time in second that the build lasted. 42 | public int totalSeconds; 43 | 44 | /// Number of errors at the end of the build. 45 | public int totalErrors; 46 | 47 | /// Number of warnings at the end of the build. 48 | public int totalWarnings; 49 | 50 | /// Result of the build. 51 | public BuildResult result; 52 | 53 | /// Constructor. 54 | public BuildEntry(BuildReport report) 55 | { 56 | BuildSummary summary = report.summary; 57 | 58 | int buildIndex = BuildData.FirstFreeIndex(); 59 | if (buildIndex != -1) 60 | { 61 | name = BuildData.GetKeyName(buildIndex); 62 | buildStartedAt = summary.buildStartedAt; 63 | platform = summary.platform; 64 | #if UNITY_2022_1_OR_NEWER 65 | totalFiles = report.GetFiles().Length; 66 | #else 67 | totalFiles = report.files.Length; 68 | #endif 69 | totalSize = summary.totalSize; 70 | totalSeconds = (int)summary.totalTime.TotalSeconds; 71 | totalErrors = summary.totalErrors; 72 | totalWarnings = summary.totalWarnings; 73 | result = summary.result; 74 | } 75 | } 76 | 77 | /// Constructor. 78 | public BuildEntry(string text) 79 | { 80 | string[] values = text.Split(','); 81 | if (values.Length == 9) 82 | { 83 | name = values[0]; 84 | buildStartedAt = new DateTime(long.Parse(values[1])); 85 | platform = StringToBuildTarget(values[2]); 86 | totalFiles = int.Parse(values[3]); 87 | totalSize = ulong.Parse(values[4]); 88 | totalSeconds = int.Parse(values[5]); 89 | totalErrors = int.Parse(values[6]); 90 | totalWarnings = int.Parse(values[7]); 91 | result = StringToBuildResult(values[8]); 92 | } 93 | } 94 | 95 | private static BuildTarget StringToBuildTarget(string target) 96 | { 97 | Enum.TryParse(target, out BuildTarget buildTarget); 98 | 99 | return buildTarget; 100 | } 101 | 102 | private static BuildResult StringToBuildResult(string build) 103 | { 104 | Enum.TryParse(build, out BuildResult buildResult); 105 | 106 | return buildResult; 107 | } 108 | 109 | public override string ToString() => $"{name}," + 110 | $"{buildStartedAt.ToBinary()}," + 111 | $"{platform}," + 112 | $"{totalFiles}," + 113 | $"{totalSize}," + 114 | $"{totalSeconds}," + 115 | $"{totalErrors}," + 116 | $"{totalWarnings}," + 117 | $"{result}"; 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildEntry.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fafe9ba32c1e4004b99fc6fb0176cd73 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildHistoryEditor.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using System; 18 | using System.IO; 19 | using System.Text; 20 | using System.Collections.Generic; 21 | using UnityEngine; 22 | using UnityEditor; 23 | using UnityEditor.Build.Reporting; 24 | 25 | namespace FronkonGames.Tools.BuildHistory 26 | { 27 | /// History and statistics of all builds. 28 | public class BuildHistoryEditor : EditorWindow 29 | { 30 | private static GUIStyle BuildEntryStyle 31 | { 32 | get 33 | { 34 | styleBuildEntry ??= new GUIStyle(GUI.skin.label) { margin = new RectOffset(0, 0, 0, 0), alignment = TextAnchor.MiddleRight }; 35 | 36 | return styleBuildEntry; 37 | } 38 | } 39 | 40 | private readonly GUIStyle stylePlatform; 41 | 42 | private Vector2 scrollPosition; 43 | 44 | private List buildHistory = new(); 45 | 46 | private static GUIStyle styleBuildEntry; 47 | 48 | private bool showChartError = true; 49 | private bool showChartDuration = false; 50 | private bool showChartFiles = false; 51 | private bool showChartSize = false; 52 | 53 | private LineChart chartError; 54 | private LineChart chartDuration; 55 | private LineChart chartFiles; 56 | private LineChart chartSize; 57 | 58 | private enum Columns 59 | { 60 | BuildResult, 61 | BuildTarget, 62 | Date, 63 | Duration, 64 | Files, 65 | Size, 66 | Errors, 67 | } 68 | 69 | private static readonly string SortColumnKey = "FronkonGames.BuildHistory.SortColumn"; 70 | private static readonly string SortColumnOrderKey = "FronkonGames.BuildHistory.SortColumnOrder"; 71 | 72 | private static readonly string ShowChartErrorsKey = "FronkonGames.BuildHistory.ShowChartErrors"; 73 | private static readonly string ShowChartDurationKey = "FronkonGames.BuildHistory.ShowChartDuration"; 74 | private static readonly string ShowChartFilesKey = "FronkonGames.BuildHistory.ShowChartFilesKey"; 75 | private static readonly string ShowChartSizeKey = "NFronkonGames.BuildHistory.ShowChartSizeKey"; 76 | 77 | [MenuItem("Window/Fronkon Games/Build History %&h")] 78 | private static void Launch() 79 | { 80 | BuildHistoryEditor buildHistoryEditor = GetWindow(); 81 | buildHistoryEditor.titleContent = new GUIContent("Build History"); 82 | buildHistoryEditor.ShowUtility(); 83 | } 84 | 85 | private void OnEnable() 86 | { 87 | buildHistory = BuildData.Load(); 88 | 89 | UpdateCharts(); 90 | 91 | SortColumn((Columns)EditorPrefs.GetInt(SortColumnKey, 0)); 92 | 93 | BuildHook.BuildFinished += BuildFinished; 94 | } 95 | 96 | private void OnDisable() 97 | { 98 | BuildHook.BuildFinished -= BuildFinished; 99 | } 100 | 101 | private void OnGUI() 102 | { 103 | GUILayout.BeginVertical("Box"); 104 | { 105 | BuildHistoryGUI(); 106 | 107 | GUILayout.FlexibleSpace(); 108 | 109 | if (buildHistory.Count > 0) 110 | ChartsGUI(); 111 | 112 | EditorHelper.Space(); 113 | 114 | GUILayout.BeginHorizontal(); 115 | { 116 | #if false 117 | if (GUILayout.Button("π", GUI.skin.label) == true) 118 | { 119 | int index = BuildData.FirstFreeIndex(); 120 | 121 | DateTime start = new(2020, 1, 1); 122 | int range = (DateTime.Today - start).Days; 123 | 124 | Array buildTargetValues = Enum.GetValues(typeof(BuildTarget)); 125 | Array buildResultValues = Enum.GetValues(typeof(BuildResult)); 126 | 127 | BuildEntry buildEntry = new(string.Empty) 128 | { 129 | name = BuildData.GetKeyName(index), 130 | buildStartedAt = start.AddDays(UnityEngine.Random.Range(0, range)).AddHours(UnityEngine.Random.Range(0, 24)).AddMinutes(UnityEngine.Random.Range(0, 60)), 131 | platform = (BuildTarget)buildTargetValues.GetValue(UnityEngine.Random.Range(0, buildTargetValues.Length)), 132 | totalFiles = UnityEngine.Random.Range(200, 600) 133 | }; 134 | buildEntry.totalSize = (ulong)buildEntry.totalFiles * (ulong)UnityEngine.Random.Range(100 * 1024, 5 * 1024 * 1024); 135 | buildEntry.totalSeconds = buildEntry.totalFiles * UnityEngine.Random.Range(15, 60); 136 | 137 | if (UnityEngine.Random.Range(0.0f, 1.0f) < 0.7f) 138 | buildEntry.result = BuildResult.Succeeded; 139 | else 140 | buildEntry.result = (BuildResult)buildResultValues.GetValue(UnityEngine.Random.Range(0, buildResultValues.Length)); 141 | 142 | buildEntry.totalErrors = buildEntry.result == BuildResult.Succeeded ? 0 : UnityEngine.Random.Range(0, 50); 143 | buildEntry.totalWarnings = UnityEngine.Random.Range(0, buildEntry.result == BuildResult.Succeeded ? 10 : 100); 144 | 145 | buildHistory.Add(buildEntry); 146 | 147 | BuildData.Save(buildEntry); 148 | 149 | UpdateCharts(); 150 | } 151 | #endif 152 | GUILayout.FlexibleSpace(); 153 | 154 | if (GUILayout.Button("Import") == true) 155 | ImportCSV(); 156 | 157 | if (GUILayout.Button("Export") == true) 158 | ExportCSV(); 159 | 160 | EditorHelper.Space(); 161 | EditorHelper.Space(); 162 | 163 | if (GUILayout.Button("Remove all builds") == true) 164 | { 165 | buildHistory.Clear(); 166 | 167 | BuildData.DeleteAll(); 168 | 169 | UpdateCharts(); 170 | } 171 | 172 | EditorHelper.Margin(); 173 | } 174 | GUILayout.EndHorizontal(); 175 | } 176 | GUILayout.EndVertical(); 177 | } 178 | 179 | private void BuildHistoryGUI() 180 | { 181 | int indexToRemove = -1; 182 | bool odd = false; 183 | 184 | GUILayout.BeginHorizontal(EditorHelper.EntryEven); 185 | { 186 | EditorHelper.Margin(); 187 | 188 | int sortColumn = EditorPrefs.GetInt(SortColumnKey, 0); 189 | int sortColumnOrder = EditorPrefs.GetInt(SortColumnOrderKey, 0); 190 | 191 | if (GUILayout.Button(sortColumn == (int)Columns.BuildResult ? (sortColumnOrder == 1 ? "\u25B2 " : "\u25BC ") : "- ", BuildEntryStyle, GUILayout.Width(25)) == true) 192 | SortColumn(Columns.BuildResult); 193 | 194 | EditorHelper.Margin(); 195 | 196 | if (GUILayout.Button(sortColumn == (int)Columns.BuildTarget ? (sortColumnOrder == 1 ? "Platform \u25B2" : "Platform \u25BC") : "Platform - ", BuildEntryStyle, GUILayout.Width(170 + 25)) == true) 197 | SortColumn(Columns.BuildTarget); 198 | 199 | GUILayout.FlexibleSpace(); 200 | 201 | if (GUILayout.Button(sortColumn == (int)Columns.Date ? (sortColumnOrder == 1 ? "Date \u25B2" : "Date \u25BC") : "Date - ", BuildEntryStyle, GUILayout.Width(70 + 40 + 30)) == true) 202 | SortColumn(Columns.Date); 203 | 204 | GUILayout.FlexibleSpace(); 205 | 206 | if (GUILayout.Button(sortColumn == (int)Columns.Duration ? (sortColumnOrder == 1 ? "Time \u25B2" : "Time \u25BC") : "Time - ", BuildEntryStyle, GUILayout.Width(40 + 25)) == true) 207 | SortColumn(Columns.Duration); 208 | 209 | GUILayout.FlexibleSpace(); 210 | 211 | if (GUILayout.Button(sortColumn == (int)Columns.Files ? (sortColumnOrder == 1 ? "Files \u25B2" : "Files \u25BC") : "Files - ", BuildEntryStyle, GUILayout.Width(50 + 25)) == true) 212 | SortColumn(Columns.Files); 213 | 214 | GUILayout.FlexibleSpace(); 215 | 216 | if (GUILayout.Button(sortColumn == (int)Columns.Size ? (sortColumnOrder == 1 ? "Size \u25B2" : "Size \u25BC") : "Size - ", BuildEntryStyle, GUILayout.Width(50)) == true) 217 | SortColumn(Columns.Size); 218 | 219 | GUILayout.FlexibleSpace(); 220 | 221 | if (GUILayout.Button(sortColumn == (int)Columns.Errors ? (sortColumnOrder == 1 ? "Errors & Warnings \u25B2" : "Errors & Warnings \u25BC") : "Errors & Warnings - ", BuildEntryStyle, GUILayout.Width(40 + 25 + EditorHelper.margin + 40 + 25)) == true) 222 | SortColumn(Columns.Errors); 223 | 224 | GUILayout.FlexibleSpace(); 225 | 226 | GUILayout.Space(25 + EditorHelper.margin); 227 | } 228 | GUILayout.EndHorizontal(); 229 | 230 | if (buildHistory.Count > 0) 231 | { 232 | scrollPosition = GUILayout.BeginScrollView(scrollPosition); 233 | { 234 | for (int i = 0; i < buildHistory.Count; ++i) 235 | { 236 | GUILayout.BeginHorizontal((odd = !odd) ? EditorHelper.EntryOdd : EditorHelper.EntryEven); 237 | { 238 | EditorHelper.Margin(); 239 | 240 | switch (buildHistory[i].result) 241 | { 242 | case BuildResult.Cancelled: GUILayout.Label(new GUIContent(EditorHelper.Cancel, "Cancelled"), GUILayout.Width(25)); break; 243 | case BuildResult.Failed: GUILayout.Label(new GUIContent(EditorHelper.Fail, "Fail"), GUILayout.Width(25)); break; 244 | case BuildResult.Succeeded: GUILayout.Label(new GUIContent(EditorHelper.Success, "Succeeded"), GUILayout.Width(25)); break; 245 | case BuildResult.Unknown: GUILayout.Label(new GUIContent(EditorHelper.Ignored, "Unknown"), GUILayout.Width(25)); break; 246 | } 247 | 248 | EditorHelper.Margin(); 249 | 250 | BuildPlatform buildPlatform = BuildPlatform.FromBuildTarget(buildHistory[i].platform); 251 | 252 | GUILayout.Label(buildPlatform.name, EditorHelper.RightLabel, GUILayout.Width(170)); 253 | GUILayout.Label(buildPlatform.ToGUIContent(), GUILayout.Width(25)); 254 | 255 | GUILayout.FlexibleSpace(); 256 | 257 | string londDate = $"{buildHistory[i].buildStartedAt.ToShortDateString()} {buildHistory[i].buildStartedAt.ToLongTimeString()}"; 258 | GUILayout.Label(new GUIContent(buildHistory[i].buildStartedAt.ToShortDateString(), londDate), EditorHelper.RightLabel, GUILayout.Width(70)); 259 | GUILayout.Label(new GUIContent(buildHistory[i].buildStartedAt.ToString("HH:mm"), londDate), EditorHelper.RightLabel, GUILayout.Width(40)); 260 | GUILayout.Label(EditorHelper.Date, GUILayout.Width(25)); 261 | 262 | GUILayout.FlexibleSpace(); 263 | 264 | TimeSpan time = TimeSpan.FromSeconds(buildHistory[i].totalSeconds); 265 | GUILayout.Label(time.ToString(@"hh\:mm"), EditorHelper.RightLabel, GUILayout.Width(40)); 266 | GUILayout.Label(EditorHelper.Clock, GUILayout.Width(25)); 267 | 268 | GUILayout.FlexibleSpace(); 269 | 270 | GUILayout.Label(new GUIContent(buildHistory[i].totalFiles.ToString(), $"{buildHistory[i].totalFiles} files"), EditorHelper.RightLabel, GUILayout.Width(50)); 271 | GUILayout.Label(EditorHelper.Files, GUILayout.Width(25)); 272 | 273 | GUILayout.FlexibleSpace(); 274 | 275 | GUILayout.Label(BytesToString(buildHistory[i].totalSize), EditorHelper.RightLabel, GUILayout.Width(50)); 276 | GUILayout.Label(EditorHelper.TotalSize, GUILayout.Width(25)); 277 | 278 | GUILayout.FlexibleSpace(); 279 | 280 | GUILayout.Label(new GUIContent(buildHistory[i].totalErrors.ToString(), $"{buildHistory[i].totalErrors} errors"), EditorHelper.RightLabel, GUILayout.Width(40)); 281 | GUILayout.Label(new GUIContent(EditorHelper.Error, $"{buildHistory[i].totalErrors} errors"), GUILayout.Width(25)); 282 | 283 | EditorHelper.Margin(); 284 | 285 | GUILayout.Label(new GUIContent(buildHistory[i].totalWarnings.ToString(), $"{buildHistory[i].totalWarnings} warnings"), EditorHelper.RightLabel, GUILayout.Width(40)); 286 | GUILayout.Label(new GUIContent(EditorHelper.Warning, $"{buildHistory[i].totalWarnings} warnings"), GUILayout.Width(25)); 287 | 288 | GUILayout.FlexibleSpace(); 289 | 290 | if (GUILayout.Button(new GUIContent(EditorHelper.ToolbarMinus, "Remove build"), EditorHelper.EntryDesc, GUILayout.Width(25)) == true) 291 | indexToRemove = i; 292 | 293 | EditorHelper.Margin(); 294 | } 295 | GUILayout.EndHorizontal(); 296 | } 297 | } 298 | GUILayout.EndScrollView(); 299 | } 300 | 301 | if (indexToRemove >= 0) 302 | { 303 | BuildData.Delete(buildHistory[indexToRemove].name); 304 | 305 | buildHistory.RemoveAt(indexToRemove); 306 | 307 | UpdateCharts(); 308 | } 309 | 310 | EditorHelper.Space(); 311 | } 312 | 313 | private void ChartsGUI() 314 | { 315 | int totalTime = 0; 316 | 317 | for (int i = 0; i < buildHistory.Count; ++i) 318 | totalTime += buildHistory[i].totalSeconds; 319 | 320 | TimeSpan totalTimeSpan = TimeSpan.FromSeconds(totalTime); 321 | TimeSpan averagelTimeSpan = TimeSpan.FromSeconds(totalTime / buildHistory.Count); 322 | 323 | showChartDuration = EditorPrefs.GetBool(ShowChartDurationKey, true); 324 | showChartDuration = GUILayout.Toggle(showChartDuration, $" Duration (min), total {totalTimeSpan:dd\\:hh\\:mm}, average {averagelTimeSpan:hh\\:mm}", GUILayout.Width(Screen.width)); 325 | EditorPrefs.SetBool(ShowChartDurationKey, showChartDuration); 326 | 327 | if (showChartDuration == true) 328 | chartDuration.DrawChart(); 329 | 330 | showChartFiles = EditorPrefs.GetBool(ShowChartFilesKey, true); 331 | showChartFiles = GUILayout.Toggle(showChartFiles, " Files", GUILayout.Width(Screen.width)); 332 | EditorPrefs.SetBool(ShowChartFilesKey, showChartFiles); 333 | 334 | if (showChartFiles == true) 335 | chartFiles.DrawChart(); 336 | 337 | showChartSize = EditorPrefs.GetBool(ShowChartSizeKey, true); 338 | showChartSize = GUILayout.Toggle(showChartSize, " Size (mb)", GUILayout.Width(Screen.width)); 339 | EditorPrefs.SetBool(ShowChartSizeKey, showChartSize); 340 | 341 | if (showChartSize == true) 342 | chartSize.DrawChart(); 343 | 344 | showChartError = EditorPrefs.GetBool(ShowChartErrorsKey, true); 345 | showChartError = GUILayout.Toggle(showChartError, " Error and Warnings", GUILayout.Width(Screen.width)); 346 | EditorPrefs.SetBool(ShowChartErrorsKey, showChartError); 347 | 348 | if (showChartError == true) 349 | chartError.DrawChart(); 350 | } 351 | 352 | private void SortColumn(Columns column) 353 | { 354 | EditorPrefs.SetInt(SortColumnKey, (int)column); 355 | 356 | int sort = EditorPrefs.GetInt(SortColumnOrderKey, 1); 357 | 358 | switch (column) 359 | { 360 | case Columns.BuildResult: buildHistory.Sort((x, y) => sort * x.result.CompareTo(y.result)); break; 361 | case Columns.BuildTarget: buildHistory.Sort((x, y) => sort * x.platform.CompareTo(y.platform)); break; 362 | case Columns.Date: buildHistory.Sort((x, y) => sort * x.buildStartedAt.CompareTo(y.buildStartedAt)); break; 363 | case Columns.Duration: buildHistory.Sort((x, y) => sort * x.totalSeconds.CompareTo(y.totalSeconds)); break; 364 | case Columns.Files: buildHistory.Sort((x, y) => sort * x.totalFiles.CompareTo(y.totalFiles)); break; 365 | case Columns.Size: buildHistory.Sort((x, y) => sort * x.totalSize.CompareTo(y.totalSize)); break; 366 | case Columns.Errors: 367 | buildHistory.Sort((x, y) => 368 | { 369 | int result = x.totalErrors.CompareTo(y.totalErrors); 370 | if (result == 0) 371 | result = x.totalWarnings.CompareTo(y.totalWarnings); 372 | 373 | return sort * result; 374 | }); 375 | break; 376 | } 377 | 378 | EditorPrefs.SetInt(SortColumnOrderKey, sort * -1); 379 | } 380 | 381 | private void UpdateCharts() 382 | { 383 | const float height = 125.0f; 384 | 385 | // Error chart. 386 | if (chartError == null) 387 | { 388 | chartError = new LineChart(this, height); 389 | chartError.data = new List[2]; 390 | chartError.data[0] = new List(); 391 | chartError.data[1] = new List(); 392 | chartError.axisLabels = new List(); 393 | 394 | chartError.colors = new List{ Color.red, Color.yellow }; 395 | } 396 | 397 | chartError.data[0].Clear(); 398 | chartError.data[1].Clear(); 399 | 400 | // Duration chart. 401 | if (chartDuration == null) 402 | { 403 | chartDuration = new LineChart(this, height); 404 | chartDuration.data = new List[1]; 405 | chartDuration.data[0] = new List(); 406 | chartDuration.axisLabels = new List(); 407 | 408 | chartDuration.colors = new List{ Color.green }; 409 | } 410 | 411 | chartDuration.data[0].Clear(); 412 | 413 | // Files chart. 414 | if (chartFiles == null) 415 | { 416 | chartFiles = new LineChart(this, height); 417 | chartFiles.data = new List[1]; 418 | chartFiles.data[0] = new List(); 419 | chartFiles.axisLabels = new List(); 420 | 421 | chartFiles.colors = new List{ Color.cyan }; 422 | } 423 | 424 | chartFiles.data[0].Clear(); 425 | 426 | // Size chart. 427 | if (chartSize == null) 428 | { 429 | chartSize = new LineChart(this, height); 430 | chartSize.data = new List[1]; 431 | chartSize.data[0] = new List(); 432 | chartSize.axisLabels = new List(); 433 | 434 | chartSize.colors = new List{ Color.magenta }; 435 | } 436 | 437 | chartSize.data[0].Clear(); 438 | 439 | // Update. 440 | List clone = buildHistory; 441 | clone.Sort((x, y) => x.buildStartedAt.CompareTo(y.buildStartedAt)); 442 | 443 | Dictionary> errorsByDay = new(); 444 | Dictionary durationByDay = new(); 445 | Dictionary filesByDay = new(); 446 | Dictionary sizeByDay = new(); 447 | 448 | for (int i = 0; i < clone.Count; ++i) 449 | { 450 | string key = clone[i].buildStartedAt.ToString("dd"); 451 | 452 | if (errorsByDay.ContainsKey(key) == true) 453 | errorsByDay[key] = new Tuple(errorsByDay[key].Item1 + clone[i].totalErrors, errorsByDay[key].Item2 + clone[i].totalWarnings); 454 | else 455 | errorsByDay.Add(key, new Tuple(clone[i].totalErrors, clone[i].totalWarnings)); 456 | 457 | if (durationByDay.ContainsKey(key) == true) 458 | durationByDay[key] += clone[i].totalSeconds / 60; 459 | else 460 | durationByDay.Add(key, clone[i].totalSeconds / 60); 461 | 462 | if (filesByDay.ContainsKey(key) == true) 463 | filesByDay[key] = (filesByDay[key] + clone[i].totalFiles) / 2; 464 | else 465 | filesByDay.Add(key, clone[i].totalFiles); 466 | 467 | if (sizeByDay.ContainsKey(key) == true) 468 | sizeByDay[key] = (sizeByDay[key] + (clone[i].totalSize / (1024 * 1024))) / 2; 469 | else 470 | sizeByDay.Add(key, clone[i].totalSize / (1024 * 1024)); 471 | } 472 | 473 | foreach (KeyValuePair> kv in errorsByDay) 474 | { 475 | chartError.data[0].Add(kv.Value.Item1); 476 | chartError.data[1].Add(kv.Value.Item2); 477 | chartError.axisLabels.Add(kv.Key); 478 | } 479 | 480 | foreach (KeyValuePair kv in durationByDay) 481 | { 482 | chartDuration.data[0].Add(kv.Value); 483 | chartDuration.axisLabels.Add(kv.Key); 484 | } 485 | 486 | foreach (KeyValuePair kv in filesByDay) 487 | { 488 | chartFiles.data[0].Add(kv.Value); 489 | chartFiles.axisLabels.Add(kv.Key); 490 | } 491 | 492 | foreach (KeyValuePair kv in sizeByDay) 493 | { 494 | chartSize.data[0].Add(kv.Value); 495 | chartSize.axisLabels.Add(kv.Key); 496 | } 497 | } 498 | 499 | private void BuildFinished(BuildReport buildReport) 500 | { 501 | buildHistory.Clear(); 502 | 503 | buildHistory = BuildData.Load(); 504 | 505 | this.Repaint(); 506 | } 507 | 508 | private void ExportCSV() 509 | { 510 | string filePath = EditorUtility.SaveFilePanel("Export", Application.dataPath, "BuildHistory.csv", "csv"); 511 | if (string.IsNullOrEmpty(filePath) == false) 512 | { 513 | if (File.Exists(filePath) == true) 514 | File.Delete(filePath); 515 | 516 | using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) 517 | { 518 | StreamWriter writer = new StreamWriter(fileStream, Encoding.UTF8); 519 | 520 | for (int i = 0; i < buildHistory.Count; ++i) 521 | writer.WriteLine(buildHistory[i].ToString()); 522 | 523 | writer.Close(); 524 | } 525 | } 526 | } 527 | 528 | private void ImportCSV() 529 | { 530 | string filePath = EditorUtility.OpenFilePanel("Inport", Application.dataPath, "csv"); 531 | if (string.IsNullOrEmpty(filePath) == false && File.Exists(filePath) == true) 532 | { 533 | using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 534 | { 535 | buildHistory.Clear(); 536 | 537 | BuildData.DeleteAll(); 538 | 539 | StreamReader reader = new StreamReader(fileStream, Encoding.UTF8); 540 | while (reader.Peek() >= 0) 541 | { 542 | string entry = reader.ReadLine(); 543 | 544 | BuildEntry buildEntry = new BuildEntry(entry); 545 | 546 | buildHistory.Add(buildEntry); 547 | 548 | BuildData.Save(buildEntry); 549 | } 550 | 551 | reader.Close(); 552 | } 553 | 554 | UpdateCharts(); 555 | } 556 | } 557 | 558 | private static string BytesToString(ulong bytes) 559 | { 560 | string[] sizes = { "B", "KB", "MB", "GB", "TB" }; 561 | 562 | int order = 0; 563 | while (bytes >= 1024 && order < sizes.Length - 1) { 564 | order++; 565 | bytes = bytes / 1024; 566 | } 567 | 568 | return $"{bytes:0.##} {sizes[order]}"; 569 | } 570 | } 571 | } -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildHistoryEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3d6ef194a5e7ba24f944361972542bd7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildHook.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using System; 18 | using UnityEditor.Build; 19 | using UnityEditor.Build.Reporting; 20 | 21 | namespace FronkonGames.Tools.BuildHistory 22 | { 23 | /// Capture build events. 24 | public class BuildHook : IPostprocessBuildWithReport 25 | { 26 | public static Action BuildFinished; 27 | 28 | public int callbackOrder => 0; 29 | 30 | public void OnPostprocessBuild(BuildReport report) 31 | { 32 | BuildData.Save(report); 33 | 34 | BuildFinished?.Invoke(report); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildHook.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa8effdbebcd22144b248fa30ea8af67 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildPlatform.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using System.Collections.Generic; 18 | using UnityEditor; 19 | using UnityEngine; 20 | 21 | namespace FronkonGames.Tools.BuildHistory 22 | { 23 | /// Build platform. 24 | public class BuildPlatform 25 | { 26 | public readonly string id; 27 | public readonly string name; 28 | 29 | private static List All 30 | { 31 | get 32 | { 33 | if (allBuildPlatforms == null) 34 | { 35 | allBuildPlatforms = new List 36 | { 37 | new("PC, Mac & Linux Standalone", "Standalone"), 38 | new("Android", "Android"), 39 | new("WebGL", "WebGL"), 40 | new("Universal Windows Platform", "Metro"), 41 | new("tvOS", "tvOS"), 42 | new("PS4", "PS4"), 43 | new("PS5", "PS5"), 44 | new("iOS", "iPhone"), 45 | new("Xbox One", "XboxOne"), 46 | new("Magic Leap OS", "Lumin"), 47 | new("Switch", "Switch"), 48 | }; 49 | } 50 | 51 | return allBuildPlatforms; 52 | } 53 | } 54 | 55 | private readonly Texture2D iconSmall; 56 | private readonly Texture2D icon; 57 | 58 | private static List allBuildPlatforms; 59 | 60 | public BuildPlatform(string name, string id) 61 | { 62 | this.name = name; 63 | this.id = id; 64 | this.iconSmall = EditorGUIUtility.IconContent($"BuildSettings.{id}.Small").image as Texture2D; 65 | this.icon = EditorGUIUtility.IconContent($"BuildSettings.{id}").image as Texture2D; 66 | } 67 | 68 | public GUIContent ToGUIContent(bool small = true) 69 | { 70 | return new GUIContent(small == true ? iconSmall : icon, name); 71 | } 72 | 73 | public static BuildPlatform FromBuildTarget(BuildTarget target) 74 | { 75 | switch (target) 76 | { 77 | case BuildTarget.StandaloneLinux64: 78 | case BuildTarget.LinuxHeadlessSimulation: 79 | case BuildTarget.StandaloneOSX: 80 | case BuildTarget.StandaloneWindows: 81 | case BuildTarget.StandaloneWindows64: return All[0]; 82 | case BuildTarget.Android: return All[1]; 83 | case BuildTarget.WebGL: return All[2]; 84 | case BuildTarget.WSAPlayer: return All[3]; 85 | case BuildTarget.tvOS: return All[4]; 86 | case BuildTarget.PS4: return All[5]; 87 | case BuildTarget.PS5: return All[6]; 88 | case BuildTarget.iOS: return All[7]; 89 | case BuildTarget.GameCoreXboxSeries: 90 | case BuildTarget.GameCoreXboxOne: 91 | case BuildTarget.XboxOne: return All[8]; 92 | case BuildTarget.Lumin: return All[9]; 93 | case BuildTarget.Switch: return All[10]; 94 | } 95 | 96 | return All[0]; 97 | } 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /Editor/BuildHistory/BuildPlatform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fc81a0e79283ca34eb5286bd2c11ec89 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildHistory/EditorHelper.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using UnityEditor; 18 | using UnityEngine; 19 | 20 | namespace FronkonGames.Tools.BuildHistory 21 | { 22 | /// Editor tools. 23 | public static class EditorHelper 24 | { 25 | public static Color PastelYellow = new(255.0f / 255.0f, 250.0f / 255.0f, 129.0f / 255.0f); 26 | public static Color PastelGreen = new(191.0f / 255.0f, 228.0f / 255.0f, 118.0f / 255.0f); 27 | public static Color PastelBlue = new(154.0f / 255.0f, 206.0f / 255.0f, 223.0f / 255.0f); 28 | public static Color PastelPurple = new(193.0f / 255.0f, 179.0f / 255.0f, 215.0f / 255.0f); 29 | public static Color PastelOrange = new(252.0f / 255.0f, 169.0f / 255.0f, 133.0f / 255.0f); 30 | 31 | // Unity Editor icons https://github.com/halak/unity-editor-icons. 32 | public static Texture2D ToolbarMinus { get { if (toolbarMinus == null) toolbarMinus = LoadEditorIcon("d_TreeEditor.Trash"); return toolbarMinus; } } 33 | public static Texture2D ToolbarPlus { get { if (toolbarPlus == null) toolbarPlus = LoadEditorIcon("Toolbar Plus"); return toolbarPlus; } } 34 | public static Texture2D Favorite { get { if (favorite == null) favorite = LoadEditorIcon("d_Favorite"); return favorite; } } 35 | public static Texture2D Files { get { if (files == null) files = LoadEditorIcon("d_Package Manager"); return files; } } 36 | public static Texture2D TotalSize { get { if (totalSize == null) totalSize = LoadEditorIcon("d_PreMatCube"); return totalSize; } } 37 | public static Texture2D Clock { get { if (clock == null) clock = LoadEditorIcon("TestStopwatch"); return clock; } } 38 | public static Texture2D Date { get { if (date == null) date = LoadEditorIcon("d_UnityEditor.AnimationWindow"); return date; } } 39 | 40 | public static Texture2D Fail { get { if (fail == null) fail = LoadEditorIcon("TestFailed"); return fail; } } 41 | public static Texture2D Ignored { get { if (ignored == null) ignored = LoadEditorIcon("TestIgnored"); return ignored; } } 42 | public static Texture2D Cancel { get { if (cancel == null) cancel = LoadEditorIcon("TestInconclusive"); return cancel; } } 43 | public static Texture2D Success { get { if (success == null) success = LoadEditorIcon("TestPassed"); return success; } } 44 | public static Texture2D Error { get { if (error == null) error = LoadEditorIcon("console.erroricon.sml"); return error; } } 45 | public static Texture2D Warning { get { if (warning == null) warning = LoadEditorIcon("console.warnicon.sml"); return warning; } } 46 | 47 | public static GUIStyle Box { get { styleBox ??= new GUIStyle(GUIStyle.none) { margin = new RectOffset(margin, margin, margin, margin) }; return styleBox; } } 48 | public static GUIStyle EntryDesc { get { styleDesc ??= new GUIStyle(GUI.skin.label) { wordWrap = true }; return styleDesc; } } 49 | public static GUIStyle EntryOdd { get { if (styleEntryOdd == null) { styleEntryOdd = new GUIStyle(GUIStyle.none) { margin = new RectOffset(0, 0, 0, 0) }; styleEntryOdd.normal.background = MakeTex(8, 8, Color.white * 0.4f); } return styleEntryOdd; } } 50 | public static GUIStyle EntryEven { get { if (styleEntryEven == null) { styleEntryEven = new GUIStyle(GUIStyle.none) { margin = new RectOffset(0, 0, 0, 0) }; styleEntryEven.normal.background = MakeTex(8, 8, Color.white * 0.1f); } return styleEntryEven; } } 51 | public static GUIStyle EntryName { get { styleEntryName ??= new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleLeft }; return styleEntryName; } } 52 | public static GUIStyle RightLabel { get { styleRightLabel ??= new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleRight }; return styleRightLabel; } } 53 | 54 | private static Texture2D toolbarMinus, toolbarPlus, favorite, files, totalSize, clock, date; 55 | private static Texture2D fail, ignored, cancel, success, error, warning; 56 | private static GUIStyle styleBox, styleEntryOdd, styleEntryEven, styleEntryName, styleRightLabel, styleDesc; 57 | 58 | public const float space = 10.0f; 59 | public const int margin = 5; 60 | 61 | /// Space. 62 | public static void Space() => GUILayout.Space(space); 63 | 64 | /// Margin. 65 | public static void Margin() => GUILayout.Space(margin); 66 | 67 | /// TextField with tooltips. 68 | public static string Text(string label, string tooltip, string text) => EditorGUILayout.TextField(new GUIContent(label, tooltip), text); 69 | 70 | /// Toggle with tooltips. 71 | public static bool Toggle(string label, string tooltip, bool value) => EditorGUILayout.Toggle(new GUIContent(label, tooltip), value); 72 | 73 | private static Texture2D LoadEditorIcon(string iconId) => EditorGUIUtility.IconContent(iconId).image as Texture2D; 74 | 75 | private static Texture2D MakeTex(int width, int height, Color col) 76 | { 77 | Color[] pix = new Color[width * height]; 78 | 79 | for (int i = 0; i < pix.Length; ++i) 80 | pix[i] = col; 81 | 82 | Texture2D result = new Texture2D(width, height); 83 | result.SetPixels(pix); 84 | result.Apply(); 85 | 86 | return result; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Editor/BuildHistory/EditorHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5faccb69bc69dd44d9a60385a529c958 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/BuildHistory/FronkonGames.Tools.BuildHistory.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FronkonGames.Tools.BuildHistory.Editor", 3 | "references": 4 | [ 5 | ], 6 | "optionalUnityReferences": [], 7 | "includePlatforms": [ 8 | "Editor" 9 | ], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": false, 12 | "overrideReferences": false, 13 | "precompiledReferences": [], 14 | "autoReferenced": true, 15 | "defineConstraints": [] 16 | } 17 | -------------------------------------------------------------------------------- /Editor/BuildHistory/FronkonGames.Tools.BuildHistory.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c9804c5b9036ec54295d58f25193ded2 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/BuildHistory/LineChart.cs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) Martin Bustos @FronkonGames 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 10 | // the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using UnityEditor; 21 | using UnityEngine; 22 | 23 | namespace FronkonGames.Tools.BuildHistory 24 | { 25 | /// Chart view mode. 26 | public enum ViewMode 27 | { 28 | NEVER, 29 | ON_SELECT, 30 | ALWAYS 31 | } 32 | 33 | /// Line chart. 34 | public class LineChart 35 | { 36 | public List[] data; 37 | 38 | public List dataLabels; 39 | 40 | public List axisLabels; 41 | 42 | public float barSpacer = 0.5f; 43 | 44 | public float xBorder = 30; 45 | public float yBorder = 20; 46 | 47 | public string formatString = "{0:0}"; 48 | public string axisFormatString = "{0:0}"; 49 | 50 | public List colors = new() { Color.magenta, Color.cyan * 2.0f, Color.green }; 51 | 52 | public Color selectedColor = EditorHelper.PastelOrange; 53 | 54 | public float depth = 0.0f; 55 | 56 | public ViewMode valueViewMode = ViewMode.ON_SELECT; 57 | public ViewMode labelViewMode = ViewMode.ALWAYS; 58 | 59 | public int gridLines = 4; 60 | 61 | public float axisRounding = 10f; 62 | 63 | public Color axisColor = Color.white; 64 | public Color fontColor = Color.white; 65 | 66 | public GUIStyle boxStyle; 67 | 68 | public float pipRadius = 3.0f; 69 | 70 | public bool drawTicks = true; 71 | 72 | private float barFloor; 73 | private float barTop; 74 | private float lineWidth; 75 | private float dataMax; 76 | 77 | private readonly EditorWindow window; 78 | private readonly float windowHeight; 79 | 80 | /// Constructor. 81 | public LineChart(EditorWindow window, float windowHeight) 82 | { 83 | this.window = window; 84 | this.windowHeight = windowHeight; 85 | 86 | dataLabels = new List(); 87 | axisLabels = new List(); 88 | } 89 | 90 | /// Draws the chart. 91 | public void DrawChart() 92 | { 93 | if (data != null && data.Length > 0) 94 | { 95 | Rect rect = GUILayoutUtility.GetRect(Screen.width, windowHeight); 96 | barTop = rect.y + yBorder; 97 | lineWidth = (float) (Screen.width - (xBorder * 2)) / data[0].Count; 98 | barFloor = rect.y + rect.height - yBorder; 99 | dataMax = 0.0f; 100 | 101 | for (int i = 0; i < data.Length; ++i) 102 | { 103 | if (data[i] != null && data[i].Count > 0) 104 | { 105 | if (data[i].Max() > dataMax) 106 | dataMax = data[i].Max(); 107 | } 108 | } 109 | 110 | // Box / border. 111 | if (boxStyle != null) 112 | GUI.Box(new Rect(rect.x + boxStyle.margin.left, rect.y + boxStyle.margin.top, 113 | rect.width - (boxStyle.margin.right + boxStyle.margin.left), 114 | rect.height - (boxStyle.margin.top + boxStyle.margin.bottom)), string.Empty, boxStyle); 115 | 116 | 117 | // Clean up variables. 118 | if (Math.Abs(dataMax % axisRounding) > float.Epsilon) 119 | dataMax = dataMax + axisRounding - (dataMax % axisRounding); 120 | 121 | // Text to Left. 122 | GUIStyle labelTextStyle = new() 123 | { 124 | alignment = TextAnchor.UpperRight 125 | }; 126 | labelTextStyle.normal.textColor = fontColor; 127 | 128 | // Draw grid lines. 129 | if (gridLines > 0) 130 | { 131 | Handles.color = Color.grey; 132 | float lineSpacing = (barFloor - barTop) / (gridLines + 1); 133 | 134 | for (int i = 0; i <= gridLines; ++i) 135 | { 136 | if (i > 0) 137 | Handles.DrawLine(new Vector2(xBorder, barTop + (lineSpacing * i)), new Vector2(Screen.width - xBorder, barTop + (lineSpacing * i))); 138 | 139 | if ((dataMax * (1 - ((lineSpacing * i) / (barFloor - barTop)))) > 0) 140 | GUI.Label(new Rect(0, barTop + (lineSpacing * i) - 8, xBorder - 2, 50), string.Format(axisFormatString, (dataMax * (1 - ((lineSpacing * i) / (barFloor - barTop))))) , labelTextStyle); 141 | } 142 | 143 | Handles.color = Color.white; 144 | } 145 | 146 | int c = 0; 147 | for (int i = 0; i < data.Length; ++i) 148 | { 149 | if (data[i] != null) 150 | { 151 | DrawLine (data[i], colors[c++], i < dataLabels.Count ? dataLabels[i] : string.Empty); 152 | 153 | if (c > colors.Count - 1) 154 | c = 0; 155 | } 156 | } 157 | 158 | // Draw Axis. 159 | Handles.color = axisColor; 160 | Handles.DrawLine(new Vector2(xBorder, barTop), new Vector2(xBorder, barFloor)); 161 | Handles.DrawLine(new Vector2(xBorder, barFloor), new Vector2(Screen.width - xBorder, barFloor)); 162 | 163 | GUIStyle centeredStyle = new() 164 | { 165 | alignment = TextAnchor.UpperCenter 166 | }; 167 | centeredStyle.normal.textColor = fontColor; 168 | 169 | // Draw ticks and labels. 170 | for (int i = 0; i < data[0].Count; ++i) 171 | { 172 | if (i > 0 && drawTicks) 173 | Handles.DrawLine(new Vector2(xBorder + (lineWidth * i), barFloor - 3), new Vector2(xBorder + (lineWidth * i), barFloor + 3)); 174 | 175 | if (i < axisLabels.Count) 176 | { 177 | Rect labelRect = new Rect(xBorder + (lineWidth * i) - lineWidth / 2.0f, barFloor + 5, lineWidth, 16); 178 | GUI.Label(labelRect, axisLabels[i], centeredStyle); 179 | } 180 | } 181 | 182 | Handles.color = Color.white; 183 | } 184 | } 185 | 186 | private void DrawLine(List data, Color color, string label) 187 | { 188 | Vector2 previousLine = Vector2.zero; 189 | Vector2 newLine; 190 | Handles.color = color; 191 | 192 | for (int i = 0; i < data.Count; ++i) 193 | { 194 | float lineTop = barFloor - ((barFloor - barTop) * (data[i] / dataMax)); 195 | newLine = new Vector2(xBorder + (lineWidth * i), lineTop); 196 | 197 | if (i > 0) 198 | Handles.DrawAAPolyLine(previousLine, newLine); 199 | 200 | previousLine = newLine; 201 | Rect selectRect = new Rect((previousLine - (Vector2.up * 0.5f)).x - pipRadius * 3, (previousLine - (Vector2.up * 0.5f)).y - pipRadius * 3, pipRadius * 6, pipRadius * 6); 202 | 203 | if (selectRect.Contains(Event.current.mousePosition)) 204 | { 205 | GUIStyle centeredStyle = new() 206 | { 207 | alignment = TextAnchor.UpperCenter 208 | }; 209 | centeredStyle.normal.textColor = fontColor; 210 | Handles.DrawSolidDisc(previousLine - (Vector2.up * 0.5f), Vector3.forward, pipRadius * 2); 211 | 212 | if (valueViewMode == ViewMode.ON_SELECT) 213 | { 214 | selectRect.y -= 16; selectRect.width += 50; selectRect.x -= 25; 215 | GUI.Label(selectRect, string.Format(formatString, data[i]), centeredStyle); 216 | } 217 | 218 | if (window != null) 219 | window.Repaint(); 220 | } 221 | else 222 | Handles.DrawSolidDisc(previousLine - (Vector2.up * 0.5f), Vector3.forward, pipRadius); 223 | } 224 | 225 | if (label != null) 226 | { 227 | GUIStyle colorStyle = new(); 228 | colorStyle.normal.textColor = color; 229 | 230 | Rect labelRect = new(previousLine.x + 8, previousLine.y - 8, 100, 16); 231 | GUI.Label(labelRect, label, colorStyle); 232 | } 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /Editor/BuildHistory/LineChart.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33804950e31050547b2f1b2ef891b186 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Martin Bustos 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 379eb359c10e62549970964bb21d0264 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Media.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 51a2c2c68c923764e8eb129ee9642c4f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Media/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FronkonGames/Build-History/a137fa963b9ac625d47fe33607dad4dd58b4f4bf/Media/banner.png -------------------------------------------------------------------------------- /Media/banner.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 592b0ea358d3a48499dfc047dfbc6768 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: Server 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: WebGL 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /Media/howto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FronkonGames/Build-History/a137fa963b9ac625d47fe33607dad4dd58b4f4bf/Media/howto.jpg -------------------------------------------------------------------------------- /Media/howto.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f48c576de2bc4ab439339f7755687887 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: Server 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: WebGL 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /Media/inspector_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FronkonGames/Build-History/a137fa963b9ac625d47fe33607dad4dd58b4f4bf/Media/inspector_0.png -------------------------------------------------------------------------------- /Media/inspector_0.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8925f179322ec0747913a661fc9a5daa 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: Server 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: WebGL 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /Media/inspector_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FronkonGames/Build-History/a137fa963b9ac625d47fe33607dad4dd58b4f4bf/Media/inspector_1.png -------------------------------------------------------------------------------- /Media/inspector_1.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 148f8f18ccd9f994ab13703fa32bcf78 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: Server 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: WebGL 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 |

5 | 6 | version 7 | 8 | 9 | license 10 | 11 | 12 | top language 13 | 14 |

15 | 16 | It keeps the information of all the builds you do and represents their data in graphs. 17 | 18 | ## 🎇 Features 19 | 20 | - Automatically records the result of each build. 21 | - Import and export data in CSV files. 22 | 23 | ## 🔧 Requisites 24 | 25 | - Unity 2021.3 or higher. 26 | 27 | ## ⚙️ Installation 28 | 29 | ### Editing your 'manifest.json' 30 | 31 | - Open the manifest.json file of your Unity project. 32 | - In the section "dependencies" add: 33 | 34 | ```c# 35 | { 36 | ... 37 | "dependencies": 38 | { 39 | ... 40 | "FronkonGames.Tools.BuildHistory": "git+https://github.com/FronkonGames/Build-History.git" 41 | } 42 | ... 43 | } 44 | ``` 45 | 46 | ### Git 47 | 48 | Just clone the repository into your Assets folder: 49 | 50 | ```c# 51 | git clone https://github.com/FronkonGames/Build-History.git 52 | ``` 53 | 54 | ### Zip 55 | 56 | Download the [latest release](https://github.com/FronkonGames/Build-History/releases) and unzip it into the Assets directory. 57 | 58 | ## 🚀 Use 59 | 60 | Simply use the menu 'Windows > Fronkon Games > Build History'. 61 | 62 |

63 | 64 | And you'll see something like this: 65 | 66 |

67 | 68 | At the top you will see a list with information about all the builds you make. The first column (1) shows whether the compilation finished successfully or not. 69 | 70 | The next column (2) shows the platform of the build. The third and fourth columns show the date (3) and how long it lasted (4). 71 | 72 | In the next two you can see how many files were processed (5) and the final size (6). The last column (8) informs you about how many errors and warnings occurred. 73 | Finally, you can click on the trash icon (8) to delete that row. 74 | 75 | By activating some of the checkbox at the bottom (9) you can activate a more visual way to see parameters such as duration, files, size and errors / warnings. 76 | 77 |

78 | 79 | Finally you can import (10) / export (11) or delete all the data (12) by clicking on any of the buttons at the bottom. 80 | 81 | ## 📜 License 82 | 83 | Code released under [MIT License](https://github.com/FronkonGames/Build-History/blob/main/LICENSE.md). 84 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a8e76e5a8886e1e4e8acb76ecb8acc8a 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /SampleScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 705507994} 41 | m_IndirectSpecularColor: {r: 0.18028378, g: 0.22571412, b: 0.30692285, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 12 47 | m_GIWorkflowMode: 1 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 0 56 | m_LightmapEditorSettings: 57 | serializedVersion: 12 58 | m_Resolution: 2 59 | m_BakeResolution: 40 60 | m_AtlasSize: 1024 61 | m_AO: 0 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_ExtractAmbientOcclusion: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 500 79 | m_PVRBounces: 2 80 | m_PVREnvironmentSampleCount: 500 81 | m_PVREnvironmentReferencePointCount: 2048 82 | m_PVRFilteringMode: 2 83 | m_PVRDenoiserTypeDirect: 0 84 | m_PVRDenoiserTypeIndirect: 0 85 | m_PVRDenoiserTypeAO: 0 86 | m_PVRFilterTypeDirect: 0 87 | m_PVRFilterTypeIndirect: 0 88 | m_PVRFilterTypeAO: 0 89 | m_PVREnvironmentMIS: 0 90 | m_PVRCulling: 1 91 | m_PVRFilteringGaussRadiusDirect: 1 92 | m_PVRFilteringGaussRadiusIndirect: 5 93 | m_PVRFilteringGaussRadiusAO: 2 94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 96 | m_PVRFilteringAtrousPositionSigmaAO: 1 97 | m_ExportTrainingData: 0 98 | m_TrainingDataDestination: TrainingData 99 | m_LightProbeSampleCountMultiplier: 4 100 | m_LightingDataAsset: {fileID: 0} 101 | m_LightingSettings: {fileID: 0} 102 | --- !u!196 &4 103 | NavMeshSettings: 104 | serializedVersion: 2 105 | m_ObjectHideFlags: 0 106 | m_BuildSettings: 107 | serializedVersion: 2 108 | agentTypeID: 0 109 | agentRadius: 0.5 110 | agentHeight: 2 111 | agentSlope: 45 112 | agentClimb: 0.4 113 | ledgeDropHeight: 0 114 | maxJumpAcrossDistance: 0 115 | minRegionArea: 2 116 | manualCellSize: 0 117 | cellSize: 0.16666667 118 | manualTileSize: 0 119 | tileSize: 256 120 | accuratePlacement: 0 121 | maxJobWorkers: 0 122 | preserveTilesOutsideBounds: 0 123 | debug: 124 | m_Flags: 0 125 | m_NavMeshData: {fileID: 0} 126 | --- !u!1 &705507993 127 | GameObject: 128 | m_ObjectHideFlags: 0 129 | m_CorrespondingSourceObject: {fileID: 0} 130 | m_PrefabInstance: {fileID: 0} 131 | m_PrefabAsset: {fileID: 0} 132 | serializedVersion: 6 133 | m_Component: 134 | - component: {fileID: 705507995} 135 | - component: {fileID: 705507994} 136 | m_Layer: 0 137 | m_Name: Directional Light 138 | m_TagString: Untagged 139 | m_Icon: {fileID: 0} 140 | m_NavMeshLayer: 0 141 | m_StaticEditorFlags: 0 142 | m_IsActive: 1 143 | --- !u!108 &705507994 144 | Light: 145 | m_ObjectHideFlags: 0 146 | m_CorrespondingSourceObject: {fileID: 0} 147 | m_PrefabInstance: {fileID: 0} 148 | m_PrefabAsset: {fileID: 0} 149 | m_GameObject: {fileID: 705507993} 150 | m_Enabled: 1 151 | serializedVersion: 10 152 | m_Type: 1 153 | m_Shape: 0 154 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 155 | m_Intensity: 1 156 | m_Range: 10 157 | m_SpotAngle: 30 158 | m_InnerSpotAngle: 21.80208 159 | m_CookieSize: 10 160 | m_Shadows: 161 | m_Type: 2 162 | m_Resolution: -1 163 | m_CustomResolution: -1 164 | m_Strength: 1 165 | m_Bias: 0.05 166 | m_NormalBias: 0.4 167 | m_NearPlane: 0.2 168 | m_CullingMatrixOverride: 169 | e00: 1 170 | e01: 0 171 | e02: 0 172 | e03: 0 173 | e10: 0 174 | e11: 1 175 | e12: 0 176 | e13: 0 177 | e20: 0 178 | e21: 0 179 | e22: 1 180 | e23: 0 181 | e30: 0 182 | e31: 0 183 | e32: 0 184 | e33: 1 185 | m_UseCullingMatrixOverride: 0 186 | m_Cookie: {fileID: 0} 187 | m_DrawHalo: 0 188 | m_Flare: {fileID: 0} 189 | m_RenderMode: 0 190 | m_CullingMask: 191 | serializedVersion: 2 192 | m_Bits: 4294967295 193 | m_RenderingLayerMask: 1 194 | m_Lightmapping: 1 195 | m_LightShadowCasterMode: 0 196 | m_AreaSize: {x: 1, y: 1} 197 | m_BounceIntensity: 1 198 | m_ColorTemperature: 6570 199 | m_UseColorTemperature: 0 200 | m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} 201 | m_UseBoundingSphereOverride: 0 202 | m_UseViewFrustumForShadowCasterCull: 1 203 | m_ShadowRadius: 0 204 | m_ShadowAngle: 0 205 | --- !u!4 &705507995 206 | Transform: 207 | m_ObjectHideFlags: 0 208 | m_CorrespondingSourceObject: {fileID: 0} 209 | m_PrefabInstance: {fileID: 0} 210 | m_PrefabAsset: {fileID: 0} 211 | m_GameObject: {fileID: 705507993} 212 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 213 | m_LocalPosition: {x: 0, y: 3, z: 0} 214 | m_LocalScale: {x: 1, y: 1, z: 1} 215 | m_ConstrainProportionsScale: 0 216 | m_Children: [] 217 | m_Father: {fileID: 0} 218 | m_RootOrder: 1 219 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 220 | --- !u!1 &963194225 221 | GameObject: 222 | m_ObjectHideFlags: 0 223 | m_CorrespondingSourceObject: {fileID: 0} 224 | m_PrefabInstance: {fileID: 0} 225 | m_PrefabAsset: {fileID: 0} 226 | serializedVersion: 6 227 | m_Component: 228 | - component: {fileID: 963194228} 229 | - component: {fileID: 963194227} 230 | - component: {fileID: 963194226} 231 | m_Layer: 0 232 | m_Name: Main Camera 233 | m_TagString: MainCamera 234 | m_Icon: {fileID: 0} 235 | m_NavMeshLayer: 0 236 | m_StaticEditorFlags: 0 237 | m_IsActive: 1 238 | --- !u!81 &963194226 239 | AudioListener: 240 | m_ObjectHideFlags: 0 241 | m_CorrespondingSourceObject: {fileID: 0} 242 | m_PrefabInstance: {fileID: 0} 243 | m_PrefabAsset: {fileID: 0} 244 | m_GameObject: {fileID: 963194225} 245 | m_Enabled: 1 246 | --- !u!20 &963194227 247 | Camera: 248 | m_ObjectHideFlags: 0 249 | m_CorrespondingSourceObject: {fileID: 0} 250 | m_PrefabInstance: {fileID: 0} 251 | m_PrefabAsset: {fileID: 0} 252 | m_GameObject: {fileID: 963194225} 253 | m_Enabled: 1 254 | serializedVersion: 2 255 | m_ClearFlags: 1 256 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 257 | m_projectionMatrixMode: 1 258 | m_GateFitMode: 2 259 | m_FOVAxisMode: 0 260 | m_SensorSize: {x: 36, y: 24} 261 | m_LensShift: {x: 0, y: 0} 262 | m_FocalLength: 50 263 | m_NormalizedViewPortRect: 264 | serializedVersion: 2 265 | x: 0 266 | y: 0 267 | width: 1 268 | height: 1 269 | near clip plane: 0.3 270 | far clip plane: 1000 271 | field of view: 60 272 | orthographic: 0 273 | orthographic size: 5 274 | m_Depth: -1 275 | m_CullingMask: 276 | serializedVersion: 2 277 | m_Bits: 4294967295 278 | m_RenderingPath: -1 279 | m_TargetTexture: {fileID: 0} 280 | m_TargetDisplay: 0 281 | m_TargetEye: 3 282 | m_HDR: 1 283 | m_AllowMSAA: 1 284 | m_AllowDynamicResolution: 0 285 | m_ForceIntoRT: 0 286 | m_OcclusionCulling: 1 287 | m_StereoConvergence: 10 288 | m_StereoSeparation: 0.022 289 | --- !u!4 &963194228 290 | Transform: 291 | m_ObjectHideFlags: 0 292 | m_CorrespondingSourceObject: {fileID: 0} 293 | m_PrefabInstance: {fileID: 0} 294 | m_PrefabAsset: {fileID: 0} 295 | m_GameObject: {fileID: 963194225} 296 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 297 | m_LocalPosition: {x: 0, y: 1, z: -10} 298 | m_LocalScale: {x: 1, y: 1, z: 1} 299 | m_ConstrainProportionsScale: 0 300 | m_Children: [] 301 | m_Father: {fileID: 0} 302 | m_RootOrder: 0 303 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 304 | -------------------------------------------------------------------------------- /SampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d50d8bd8682e366439649a7fc1389e5c 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.frokongames.tools.buildhistory", 3 | "version": "1.0.1", 4 | "displayName": "Build History", 5 | "description": "Keep a history of all the builds you do.", 6 | "references": [ ], 7 | "unity": "2021.3", 8 | "unityRelease": "26f1", 9 | "optionalUnityReferences": [], 10 | "includePlatforms": [], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": false, 13 | "overrideReferences": false, 14 | "precompiledReferences": [], 15 | "autoReferenced": true, 16 | "defineConstraints": [], 17 | "dependencies": { }, 18 | "keywords": [ "unity", "unity3d", "gamedev", "gamedevelopment", "indiedev", "build", "history", "tool", "csv" ], 19 | "category": "Unity", 20 | "repository": 21 | { 22 | "type": "git", 23 | "url": "git+https://github.com/FronkonGames/Build-History.git" 24 | }, 25 | "author": 26 | { 27 | "name": "Martin Bustos", 28 | "email": "fronkongames@gmail.com", 29 | "url": "https://fronkongames.github.io/" 30 | } 31 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c4b414d3b9c390b48a0e1067ed776329 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------