当前位置:   article > 正文

Unity编辑器工具制作(二)——制作一键打包工具_unity一键打包 packagegui

unity一键打包 packagegui

前言

前一篇主要将如何建立自己的工具箱,此篇主要讲如何往工具箱里添加工具,并以一键打包为例,探寻一种通用的工具制作流程

1.明确数据对象

要做工具首先要明确我们工具要处理的对象是谁,即数据是谁,数据的形式是怎样的,数据的量,数据的来源,甚至于数据的结构,就像是厨子要做一道菜,第一件事是选择食材,处理食材。

对数据的处理不是一下子就完成的,可能在工具制作过程中,我们会对数据有新的要求,比如命名,数据结构,数据路径要求等等,这些要求是对数据的规范,只有统一的,有规律的,有秩序的数据才能被工具批处理。

以一键打包为例,我们首先确定我们都有哪些数据,一键打包不需要处理数据,但是需要获得数据,我们需要获取打包平台,打包路径,CompanyName,ProductName,编译器选择(mono,IL2Cpp),甚至于安卓SDK的版本,场景等等。

确定数据的方式就是我们打包过程中需要调整的数据,把这些需要调整的数据放到一个页面,并且打包按钮也放过来,在一个页面就可以完成打包,这就是一键打包工具的最终目标。

参考代码:
将要使用的数据声明好
使用GetSet访问器 灵活控制在工具界面是否可读可写

 private string androidPath;
 private string winPath;
 private List<string> m_activeScene = new List<string>();
 public AndroidSdkVersions androidSDK_Version
    {
        get
        {
            return buildConfig.androidSDK_Version;
        }
        set
        {
            if (buildConfig != null)
            {
                buildConfig.androidSDK_Version = value;
            }
        }
    }
     public int versionIncrease
    {
        get
        {
            if (buildConfig == null)
            {
                return 0;
            }
            return buildConfig.versionIncrease;
        }
        set
        {
            if (buildConfig != null)
            {
                buildConfig.versionIncrease = value;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

2.明确数据处理流程

数据有了,然后是明确数据处理的流程,这里可以画一个流程图,可以清楚地知道数据是如何一步一步变成我们想要的样子,中间过程可能需要组合新的数据,数据与数据之间进行关联等等

以一键打包为例
在这里插入图片描述
决定数据处理流程的很大程度是 相关API调用决定的,比如打包的核心API:BuildPipeline.BuildPlayer
我们去unity手册可以知道它需要哪些参数,这些参数决定了数据处理流程,当一个工具要调用不同的API时,对数据的要求也不同,条件允许情况下可以寻找更合适的API,从而精简数据处理流程(Nuget,Gethub,很多轮子,工具的意义是省时省力,所以为工具造轮子,本末倒置,反而加大开发难度,增加开发时长)

3.对数据结果负责,对结果正确性检测

对数据结果进行检测,检查是否正确的处理数据,使数据达到我们的处理要求。
以一键打包为例,检测打包后的APP是否正常使用,多次打包是否正常,换项目打包是否正常。
在Odin中要执行功能,一般使用button,在我们的打包函数上,添加特性标签Button,即可显示在工具页面,点击即可执行打包函数
参考代码:

/// <summary>
    /// 打包
    /// </summary>
    [TitleGroup("PlayerSetting 打包设置")]
    [Button(90), GUIColor(0.4f, 0.8f, 1f)]
    public void BuildPackage()
    {
        //this.isBuild = !this.isBuild;
        PlayerSettings.companyName = companyName;
        PlayerSettings.productName = productName;
        PlayerSettings.bundleVersion = version;

        PlayerSettings.Android.targetSdkVersion = androidSDK_Version;

        #region 开始打包

        isBuilding = BuildPipeline.isBuildingPlayer;

        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        buildPlayerOptions.scenes = activeScenes.ToArray();
        //打包目标路径
        androidPath = buildPath + @"\" + buildTarget.ToString() + @"\" + productName + "_" + version + ".apk";
        winPath = buildPath + @"\" + buildTarget.ToString() + @"\" + productName + "_" + version + ".exe";
        if (buildTarget == BuildTargetGroup.Android)
        {
            buildPlayerOptions.locationPathName = androidPath;
        }
        else
        {
            buildPlayerOptions.locationPathName = winPath;
        }
        //打包目标平台
        if (buildTarget.ToString() == "Standalone")
        {
            buildPlayerOptions.target = BuildTarget.StandaloneWindows;
        }
        else
        {
            buildPlayerOptions.target = (BuildTarget)Enum.Parse(typeof(BuildTarget), buildTarget.ToString());
        }

        buildPlayerOptions.options = BuildOptions.None;
        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
        BuildSummary summary = report.summary;
        if (summary.result == BuildResult.Succeeded)
        {
            Debug.Log("Build succeeded: " + summary.platform);
            Debug.Log("Build succeeded: " + summary.totalSize + "bytes");
            Debug.Log("Build succeeded: " + summary.totalTime + "s");

            SucessBuild(report);
        }

        if (summary.result == BuildResult.Failed)
        {
            Debug.Log("Build failed");
            FailBuild(report);
        }
        isBuilding = BuildPipeline.isBuildingPlayer;

        #endregion 
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

4.增加程序鲁棒性

鲁棒是Robust的音译,强壮,健壮,为了使我们的工具更加好用,要进行数据的合理性检测,确保在无效的,错误的数据情况下,工具也不会崩溃(unity自个都会莫名其妙崩溃)
检测最多的是针对String的检测,空字符,空引用等等
还有对数组的检测,空数组,空引用
一些清况下 还需要使用 try catch 包裹

 string.IsNullOrEmpty()//同时检测空字符和空引用
  • 1

错误用法:

      if(testString==""||testString==null)
        {
            return;
        }
  • 1
  • 2
  • 3
  • 4

空字符和空引用在同一个if的情况下,如果是空引用,会error
因为这两句都会执行,但是空引用不能检测空字符,会出现null

5.代码重构

重构不必多说,细节之中自有天地
推荐插件CodeMain,打开码锹窗口
在这里插入图片描述
码锹会列出你所有的属性,字段,枚举,方法等,右边的数字如果红色,就意味着可读性差,重构之
如果能通过看码锹就完整的在脑子里能跑通整个流程,说明就可以了。

在这里插入图片描述
重构永无止境,细节之中自有天地

完整代码参考:

using Sirenix.OdinInspector;
using Sirenix.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;

[TypeInfoBox("<size=20>一键打包</size>")]
public class OneKeyBuildlEditor : GlobalConfig<OneKeyBuildlEditor>
{
    private const int addressableBundlesDirOrder = 3;
    private const int AddressableButtonOrder = 2;
    private const int Addressables地址管理Order = 3;
    private const int PlayerSetting打包设置Order = 4;
    private const int PlayerSetting打包结果Order = 5;
    private const string HFS = "HFS";
    private string androidPath;
    private string winPath;
    private bool isBuild = false;

   

    [TitleGroup("PlayerSetting 打包设置", null, TitleAlignments.Left, true, true, false, PlayerSetting打包设置Order)]
    public BuildConfig buildConfig;

    private List<string> m_activeScene = new List<string>();

    [ShowInInspector]
    [TitleGroup("PlayerSetting 打包设置", null, TitleAlignments.Left, true, true, false, PlayerSetting打包设置Order)]
    public List<string> activeScenes
    {
        get
        {
            var tempScenes = new List<string>();
            EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
            foreach (var item in scenes)
            {
                if (item.enabled)
                {
                    tempScenes.Add(item.path);
                }
            }
            return tempScenes;
        }
    }

    private bool isBuilding;

    [TitleGroup("PlayerSetting 打包设置")]
    public AndroidSdkVersions androidSDK_Version
    {
        get
        {
            return buildConfig.androidSDK_Version;
        }
        set
        {
            if (buildConfig != null)
            {
                buildConfig.androidSDK_Version = value;
            }
        }
    }

    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    [FolderPath(AbsolutePath = true)]
    public string buildPath
    {
        get
        {
            if (buildConfig == null)
            {
                return "";
            }
            return buildConfig.buildPath;
        }
        set
        {
            if (buildConfig != null)
            {
                buildConfig.buildPath = value;
            }
            EditorPrefs.SetString("OdinBuild.BuildPath", value);
        }
    }

    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    [ReadOnly]
    public BuildTargetGroup buildTarget
    {
        get
        {
            if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
            {
                return BuildTargetGroup.Android;
            }
            else if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
            {
                return BuildTargetGroup.iOS;
            }
            else
            {
                return BuildTargetGroup.Standalone;
            }
        }
    }

    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    public string companyName
    {
        get
        {
            return PlayerSettings.companyName;
        }
        set
        {
            PlayerSettings.companyName = value;
        }
    }

    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    public string productName
    {
        get
        {
            return PlayerSettings.productName;
        }
        set
        {
            PlayerSettings.productName = value;
        }
    }

    [ReadOnly]
    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    [InfoBox("将要打包的版本号(不可修改,默认自增)")]
    public string version
    {
        get
        {
            return PlayerSettings.bundleVersion;
        }
        set
        {
            PlayerSettings.bundleVersion = value;
        }
    }

    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    public int versionIncrease
    {
        get
        {
            if (buildConfig == null)
            {
                return 0;
            }
            return buildConfig.versionIncrease;
        }
        set
        {
            if (buildConfig != null)
            {
                buildConfig.versionIncrease = value;
            }
        }
    }

    [TitleGroup("PlayerSetting 打包设置"), ShowInInspector]
    public ScriptingImplementation scriptingBacked
    {
        get
        {
            ScriptingImplementation ScriptingBackend = PlayerSettings.GetScriptingBackend(buildTarget);
            return ScriptingBackend;
        }
        set
        {
            PlayerSettings.SetScriptingBackend(buildTarget, value);
        }
    }

    /// <summary>
    /// 打包
    /// </summary>
    [TitleGroup("PlayerSetting 打包设置")]
    [Button(90), GUIColor(0.4f, 0.8f, 1f)]
    public void BuildPackage()
    {
        //this.isBuild = !this.isBuild;
        PlayerSettings.companyName = companyName;
        PlayerSettings.productName = productName;
        PlayerSettings.bundleVersion = version;

        PlayerSettings.Android.targetSdkVersion = androidSDK_Version;

        #region 开始打包

        isBuilding = BuildPipeline.isBuildingPlayer;

        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        buildPlayerOptions.scenes = activeScenes.ToArray();
        //打包目标路径
        androidPath = buildPath + @"\" + buildTarget.ToString() + @"\" + productName + "_" + version + ".apk";
        winPath = buildPath + @"\" + buildTarget.ToString() + @"\" + productName + "_" + version + ".exe";
        if (buildTarget == BuildTargetGroup.Android)
        {
            buildPlayerOptions.locationPathName = androidPath;
        }
        else
        {
            buildPlayerOptions.locationPathName = winPath;
        }
        //打包目标平台
        if (buildTarget.ToString() == "Standalone")
        {
            buildPlayerOptions.target = BuildTarget.StandaloneWindows;
        }
        else
        {
            buildPlayerOptions.target = (BuildTarget)Enum.Parse(typeof(BuildTarget), buildTarget.ToString());
        }

        buildPlayerOptions.options = BuildOptions.None;
        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
        BuildSummary summary = report.summary;
        if (summary.result == BuildResult.Succeeded)
        {
            Debug.Log("Build succeeded: " + summary.platform);
            Debug.Log("Build succeeded: " + summary.totalSize + "bytes");
            Debug.Log("Build succeeded: " + summary.totalTime + "s");

            SucessBuild(report);
        }

        if (summary.result == BuildResult.Failed)
        {
            Debug.Log("Build failed");
            FailBuild(report);
        }
        isBuilding = BuildPipeline.isBuildingPlayer;

        #endregion 开始打包
    }
    /// <summary>
    /// 打包失败
    /// </summary>
    /// <param name="report"></param>
    private void FailBuild(BuildReport report)
    {
        buildResult = "打包失败,请看Console信息";
    }

    /// <summary>
    /// 打包成功
    /// </summary>
    /// <param name="report"></param>
    private void SucessBuild(BuildReport report)
    {
        BuildSummary summary = report.summary;
        string size = "检测打包大小失败";
        if (File.Exists(androidPath))
        {
            FileInfo apk = new FileInfo(androidPath);
            size = " " + (apk.Length / (1024.00 * 1024.00)).ToString("f2") + "MB";
        }
        else
        {
            long m_size = 0;
            GetDirSizeByPath((buildPath + @"\" + buildTarget.ToString()).Replace(@"/", @"\"), ref m_size);
            size = " " + (m_size / (1024.00 * 1024.00)).ToString("f2") + "MB";
        }

        string time = " " + summary.totalTime + "s";
        buildResult = "打包成功: " + summary.outputPath + "\n" +
            "安装后大小: " + size + "\n" +
            "打包时长: " + time + "\n";

        string[] versionsNum = PlayerSettings.bundleVersion.Split('.');
        int tempInt = int.Parse(versionsNum[2]) + versionIncrease;
        versionsNum[2] = tempInt.ToString();
        var tempVersionsNum = String.Join(".", versionsNum);

        PlayerSettings.bundleVersion = tempVersionsNum;
        EditorUtility.OpenWithDefaultApp(buildPath.Replace(@"/", @"\"));
    }

    [TitleGroup("PlayerSetting 打包结果", null, TitleAlignments.Left, true, true, false, PlayerSetting打包结果Order)]
    [ReadOnly]
    [MultiLineProperty(3), ShowInInspector]
    public string buildResult
    {
        get
        {
            return EditorPrefs.GetString("OdinBuild.buildResult");
        }
        set
        {
            EditorPrefs.SetString("OdinBuild.buildResult", value);
        }
    }

    /// <summary>
    /// 获取文件夹的大小
    /// </summary>
    /// <param name="dir">文件夹目录</param>
    /// <param name="dirSize">返回文件夹大小,传递引用</param>
    private static void GetDirSizeByPath(string dir, ref long dirSize)
    {
        try
        {
            DirectoryInfo dirInfo = new DirectoryInfo(dir);

            DirectoryInfo[] dirs = dirInfo.GetDirectories();
            FileInfo[] files = dirInfo.GetFiles();

            foreach (var item in dirs)
            {
                GetDirSizeByPath(item.FullName, ref dirSize);
            }

            foreach (var item in files)
            {
                dirSize += item.Length;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("获取文件大小失败" + ex.Message);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
public class BuildConfig : SerializedScriptableObject
{
    public AndroidSdkVersions androidSDK_Version;
    public BuildTarget buildTarget;

    public string buildPath;

   
    public int versionIncrease;

    //public string buildResult;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
public class MyOdin : OdinMenuEditorWindow
    {
        [MenuItem("Tools/我的工具箱")]
        private static void OpenWindow()
        {
            var window = GetWindow<MyOdin>();
            window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1000, 500);
        }

        protected override OdinMenuTree BuildMenuTree()
        {
            OdinMenuTree tree = new OdinMenuTree();
            tree.Add("一键打包工具", OneKeyBuildlEditor.Instance, EditorIcons.SmartPhone);
            return tree;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/115326
推荐阅读
相关标签
  

闽ICP备14008679号