游戏多平台分支与打包策略

游戏开发,免不了要上多个平台,如googleplay、appstore等,这就涉及到了游戏源码版本管理与分支设计问题。下面我们来探讨其中一种版本管理方式,没有对错,只有适不适合当下,另外更希望能对大家起到抛砖引玉的作用。

笔者认为,版本管理是服务于团队、完善工作流程、解决某些问题、提升团队效率而提出来的一种工作流,千万不要为了"版本管理"而"版本管理",无端的去多做一些无谓的工作而增加团队的负担。这一点特别体现在应用源码版本管理工具上,有git、svn、pscm等,我们要根据项目大小、团队成员数量、应用工具水平等因素选择,而不能一味的追求某种工具,而让团队增加没必要的学习成本和应用成本。切记工具是为人服务的,而不是人服务于工具。下面,以笔者个人最近的一个项目为例,我们一起探讨一下基于 Unity 官方版本管理软件 Plastic SCM下的游戏多平台分支与打包策略。

首先上一张分支图,如下:

如上图,项目工程只有一个main 主分支,所有的源码都是以这个主分支为准。有多个平台的,可以依赖于主分支创建对应的平台分支,就像上面的Amazon\googleplay\ios分支等。主分支的开发和修改原则是,只包含项目与平台无关的代码。平台分支的开发和修改原则是,只包含主分支与平台相关的代码。当主分支有修改时,可以正向合并到其它分去上,这样就能保证一份代码各个平台通用。与平台相关的,可以在对应平台的分支下开发和修改。

我们要遵守一个原则,我们只能从主分支正向合并到其它平台分支,并不能从平台分支逆向合并到主分支。如果能逆向合并,创建平台分支的意义也就不存在了,因为主分支最终会包含所有分支的内容。

我们平时如果需要出包,那就看情况,如果只是普通的测试包,那就主分支直接出包好了。如果需要各个平台分别出包,合并后,那就对应分支下打包就好了,相对来说还是十分简单有效的。

上图只是解决了多平台分支的问题,肯定有小伙伴会问,如果多人协作开发,单一个主分支可以吗?当然不可以啊,思路其实就和多平台分支一样,分别依赖于主分支创建各个开发者所需分支就行了,但开发者分支与平台分支合并的原则是不一样的。开发者分支合并的原则是先拉取,再合并/推送,也就是可以正向/逆向合并。

通过上述操作,基本上解决了多人协作、多个平台发布、多个源码版本的管理问题,剩下的就是各个平台发布出包问题了。

各个平台如果需要同时发布出包,我们如果只有一台打包机器的情况下,需要手动设置的发布内容还是挺多的,而且容易出错。在此和大家探讨一种半自动化的方式,思路上主要作为抛砖引玉,让大家能找到适合自己的出包方式。如利用Jenkins的工作流,编写脚本全自动的出包方式等。

** 笔者目前采用的方式是半自动化的,因为出包需求并不是十分频繁。我们先上一个自动化脚本,如下图:**

ini 复制代码
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using UnityEditor;

namespace Simple.Build
{
    //包体属性配置文件对象
    public class PackageConfig
    {
        public string CompanyName;
        public string ProductName;
        public string PackageName;
        /// <summary>
        /// 新版本号,只能3位(Ios 限制)
        /// </summary>
        public string NewVersion;
        /// <summary>
        /// 最后一次打包的版本号,会在此基础上递增
        /// </summary>
        public string LastBuildVersion;
        /// <summary>
        /// 版本代号,会在此基础上递增
        /// </summary>
        public int VersionCode;
        /// <summary>
        /// 安卓KeyStore文件
        /// </summary>
        public string AndroidKeyStoreFile = "";
        public string AndroidKeyStorePass = "";

        public string AndroidAliasName = "";
        public string AndroidAliasPass = "";


    }

    //包体设置对象
    public class PackageSetting
    {
        //保存包体配置的json文件
        private const string _configFile = "Assets/Scripts/Editor/BuildProcess/PackageConfig.json";
        private static PackageConfig _config;
        public static PackageConfig Config
        {
            get
            {
                if( _config == null )
                {
                    if(!File.Exists(_configFile))
                    {
                        _config = new PackageConfig();
                        _config.CompanyName = "CompanyName";
                        _config.ProductName = "ProductName";
                        _config.PackageName = "com.PackageName";
                        _config.NewVersion = "1.0.0";
                        _config.LastBuildVersion = "1.0.0";
                        _config.VersionCode = 1;                       
                    }
                    else
                    {
                        var content = File.ReadAllText(_configFile);
                        _config = JsonConvert.DeserializeObject<PackageConfig>( content );
                    }
                }
                return _config;
            }
        }
        public static void ClearConfigCache() { _config = null; }

        public static void Save()
        {
            var content = JsonConvert.SerializeObject(_config, Formatting.Indented);
            File.WriteAllText(_configFile, content);
        }

        public static void SetCompanyName(string name)
        {
            Config.CompanyName = name;
        }
 
        public static void SetProductName(string name)
        { 
            Config.ProductName = name;
        }

        public static void SetNewVersion(string version)
        {
            Config.NewVersion = version;
        }

        public static void SetLastBuildVersion(string version)
        { 
            Config.LastBuildVersion = version;
        }
        //获取打包版本号,自动设置新版本号(version)
        public static string GetBuildVersion()
        {         
            if(!string.IsNullOrEmpty(Config.NewVersion))
            {//如果新版本号不为号,则以新版本号为基础
                var v = Config.NewVersion;
                var varr = v.Split('.').ToList();      
                //修正回3位的版本号
                if(varr.Count > 3)
                {
                    for(int i = varr.Count - 1; i > 2; i--)
                    {
                        varr.RemoveAt(i);
                    }
                }
                else if(varr.Count < 3)
                {
                    for (int i = varr.Count; i < 3; i++)
                    {
                        varr.Add("0");
                    }
                }                              
                var nv = string.Join(".", varr);
                return nv;
            }
            else
            {//最后一次打包的版本号加1
                var varr = Config.LastBuildVersion.Split('.');
                var ncode = int.Parse(varr[2]) + 1;
                varr[2] = ncode.ToString();
                var nv = string.Join(".", varr);             
                return nv;
            }            
        }
        //获取最后一次打包的版本号(versionCode)
        public static int GetBuildVersionCode()
        {
            return Config.VersionCode + 1;
        }

    }
}

上面的代码只要是对安卓包体的包名、标识符、versionCode(ios是buildNum)、密码等必须属性进行设置,并保存在json文件中。这样如果我们需要改这些属性时,只要改json文件即可,其它的都不需要理会。比如在我们切换分支到某个目标平台时,ProjectSetting文件下保存的包体包名versionCode等这些信息有可能会发生改变,而这些信息有可能不是当下分支平台的信息,这时还要检查或再设置一次,十分麻烦。有了上面的脚本,我们就不需要理会这一步了。

下面我们再写一个脚本,让出包的时候调用上面的脚本设置包体,如下图:

ini 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using Simple.Build;
using System.IO;
using Excel2json;

/// <summary>
/// googleplay平台打包设置
/// </summary>
public class BPGooglePlayPackageSet : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
    public int callbackOrder => 10;
  
    //打包前设置包体各个属性
    public void OnPreprocessBuild(BuildReport report)
    {
        if (report.summary.platform != BuildTarget.Android)
            return;
        
        PackageSetting.ClearConfigCache();

        var config = PackageSetting.Config;
        PlayerSettings.Android.useCustomKeystore = true;
        PlayerSettings.Android.keystoreName = config.AndroidKeyStoreFile;
        PlayerSettings.Android.keystorePass = config.AndroidKeyStorePass;

        PlayerSettings.Android.keyaliasName = config.AndroidAliasName;
        PlayerSettings.Android.keyaliasPass = config.AndroidAliasPass;

        PlayerSettings.companyName = config.CompanyName;
        PlayerSettings.productName = config.ProductName;
        PlayerSettings.applicationIdentifier = config.PackageName;

        PlayerSettings.bundleVersion = PackageSetting.GetBuildVersion();
        PlayerSettings.Android.bundleVersionCode = PackageSetting.GetBuildVersionCode();
        
    }
    ///打包后保存设置
    public void OnPostprocessBuild(BuildReport report)
    {
        Debug.Log($"--------------Ios CompanyName {PlayerSettings.companyName}");
        Debug.Log($"--------------Ios ProductName {PlayerSettings.productName}");
        Debug.Log($"--------------Ios PackageName {PlayerSettings.applicationIdentifier}");
        Debug.Log($"--------------Ios Version {PlayerSettings.bundleVersion}");
        Debug.Log($"--------------Ios VersionCode {PlayerSettings.Android.bundleVersionCode}");

        //更新配置文件
        var config = PackageSetting.Config;
        config.LastBuildVersion = PlayerSettings.bundleVersion;
        config.VersionCode = PlayerSettings.Android.bundleVersionCode;
        config.NewVersion = "";
        PackageSetting.Save();
    }
}

将上面的所有脚本放在Editor文件夹下,这样只要各个平台打包发布时,都会自动的发布正确的包体了。

下面是ios平台分支的脚本:

ini 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using Simple.Build;

/// <summary>
/// appstore平台打包设置
/// </summary>
public class BPAppStorePackageSet : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
    public int callbackOrder => 10;
  

    public void OnPreprocessBuild(BuildReport report)
    {
        if (report.summary.platform != BuildTarget.iOS)
            return;

        PackageSetting.ClearConfigCache();

        var config = PackageSetting.Config;      
        PlayerSettings.companyName = config.CompanyName;
        PlayerSettings.productName = config.ProductName;
        PlayerSettings.applicationIdentifier = config.PackageName;

        PlayerSettings.bundleVersion = PackageSetting.GetBuildVersion();
        PlayerSettings.iOS.buildNumber = PackageSetting.GetBuildVersionCode().ToString();
    
    }

    public void OnPostprocessBuild(BuildReport report)
    {
        Debug.Log($"--------------Ios CompanyName {PlayerSettings.companyName}");
        Debug.Log($"--------------Ios ProductName {PlayerSettings.productName}");
        Debug.Log($"--------------Ios PackageName {PlayerSettings.applicationIdentifier}");
        Debug.Log($"--------------Ios Version {PlayerSettings.bundleVersion}");
        Debug.Log($"--------------Ios VersionCode {PlayerSettings.iOS.buildNumber}");

        //更新配置文件
        var config = PackageSetting.Config;
        config.LastBuildVersion = PlayerSettings.bundleVersion;
        config.VersionCode = int.Parse(PlayerSettings.iOS.buildNumber);
        config.NewVersion = "";
        PackageSetting.Save();
    }
}

部分自动化脚本布局如下图:

这样,我们从分支策略到团队协作,到多平台发布出包,基本上就探讨完毕了,希望对大家起到抛夸引玉的作用。

相关推荐
技术小甜甜8 小时前
【Blender Texture】【游戏开发】高质感 Blender 4K 材质资源推荐合集 —— 提升场景真实感与美术表现力
blender·游戏开发·材质·texture
Thomas游戏开发14 小时前
Unity3D TextMeshPro终极使用指南
前端·unity3d·游戏开发
Thomas游戏开发2 天前
Unity3D 逻辑代码性能优化策略
前端框架·unity3d·游戏开发
Thomas游戏开发3 天前
Unity3D HUD高性能优化方案
前端框架·unity3d·游戏开发
陈哥聊测试4 天前
游戏公司如何同时管好上百个游戏项目?
游戏·程序员·游戏开发
一名用户5 天前
unity随机生成未知符号教程
c#·unity3d·游戏开发
Be_Somebody9 天前
计算机图形学——Games101深度解析_第二章
游戏开发·计算机图形学·games101
GameTomato10 天前
【IOS】【OC】【应用内打印功能的实现】如何在APP内实现打印功能,连接本地打印机,把想要打印的界面打印成图片
macos·ios·objective-c·xcode·游戏开发·cocos2d
Be_Somebody10 天前
计算机图形学——Games101深度解析_第一章
游戏开发·计算机图形学·games101
Thomas_YXQ16 天前
Unity3D HUD UI性能优化方案
开发语言·ui·搜索引擎·性能优化·全文检索·unity3d