Unity-android/iOS自动设置ProjectSetting
来源:互联网 发布:java写入日志文件 编辑:程序博客网 时间:2024/05/16 08:21
工作中碰到由同一个项目,打包中不同包名的游戏apk或ipa的需求,比如项目Trunk1.0,需要打出一个包名为com.xx.xx.a的包,之后可能又需要打出包名为com.xx.xx.b的包。而每切换打新的包,就需要更改
Product Name,Default Icon,Icon,BundleId,等等(如下图 所示)
所以为了更有效率打包,写个编辑器一键设置这些参数势在必行。
写工具前先明白有哪些参数设置是包与包之前不同之处。
这里针对我这个项目来介绍下:
主要改动的方面:
- ProjectSetting
- SDK相关
配置表的设计
通过将动态改变的参数放到配置表中
这里我采取的是Excel来配置,读取using Excel;
`public class Menu{ public string ID = ""; public string bundle_identifier = ""; public string company_name = ""; public string product_name = ""; public string keyaliasName = ""; public string keyaliasPass = ""; public string keystoreName = ""; public string keystorePass = ""; public string WXAppID = ""; public string scheme = "";}public class ExcelAccess { public static string ExcelName = "ProjectSetting.xlsx"; public static string[] SheetNames = { "Sheet1" }; public static List<Menu> SelectMenuTable(int tableId,int appId) { DataRowCollection collect = ExcelAccess.ReadExcel(SheetNames[tableId - 1]); List<Menu> menuArray = new List<Menu>(); for (int i = 1; i < collect.Count; i++) { if (i < 3) continue; if(appId != 0) { if (collect[i][0].ToString() != appId.ToString()) continue; } Menu menu = new Menu(); menu.ID = collect[i][0].ToString(); menu.bundle_identifier = collect[i][1].ToString(); menu.company_name = collect[i][2].ToString(); menu.product_name = collect[i][3].ToString(); menu.keyaliasName = collect[i][4].ToString(); menu.keyaliasPass = collect[i][5].ToString(); menu.keystoreName = collect[i][6].ToString(); menu.keystorePass = collect[i][7].ToString(); menu.WXAppID = collect[i][8].ToString(); menu.scheme = collect[i][9].ToString(); menuArray.Add(menu); } return menuArray; } /// <summary> /// 读取 Excel 需要添加 Excel; System.Data; /// </summary> /// <param name="sheet"></param> /// <returns></returns> static DataRowCollection ReadExcel(string sheet) { FileStream stream = File.Open(FilePath(ExcelName), FileMode.Open, FileAccess.Read, FileShare.Read); IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream); DataSet result = excelReader.AsDataSet(); return result.Tables[sheet].Rows; } static string FilePath(string name) { string path = Application.dataPath + "/ExcelConfig/" + name; return path; }`
修改ProjectSetting
1.这里先介绍我们友好的编辑器界面:
记录选中的游戏是哪一款,然后用一个唯一id记录下来,当我点击 设置 按钮,把唯一id记录在txt文件:
static public void ChangeAppId() { Debug.Log("--------------------------ChangeAppId--------------------------------------"); if (menu != null) { string path = Application.dataPath + "/Resources/AppId.txt"; FileStream fs = new FileStream(path, FileMode.Create); StreamWriter sw = new StreamWriter(fs); //开始写入 sw.Write(menu.ID); //清空缓冲区 sw.Flush(); //关闭流 sw.Close(); fs.Close(); Debug.Log("【新的AppId】:" + menu.ID); } }
2.根据选中哪个游戏,对应动态修改ProjectSetting
menu为选中这款游戏在配置表里对应的数据
数据的内容格式:
PlayerSettings.bundleIdentifier = menu.bundle_identifier; PlayerSettings.companyName = menu.company_name; PlayerSettings.productName = menu.product_name; PlayerSettings.defaultInterfaceOrientation = UIOrientation.LandscapeLeft;
Android和iOS在projectSetting设置有些区别(Android设置Device,打包的脚本,签名,而ios没有签名这一项)
Android上:
static public void SetAndroidProject(Menu menu) { if (isFormalPackage) { PlayerSettings.Android.targetDevice = AndroidTargetDevice.FAT; PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP); } else { PlayerSettings.Android.targetDevice = AndroidTargetDevice.ARMv7; PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.Mono2x); } PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel9; PlayerSettings.Android.keyaliasName = menu.keyaliasName; PlayerSettings.Android.keyaliasPass = menu.keyaliasPass; PlayerSettings.Android.keystoreName = menu.keystoreName; PlayerSettings.Android.keystorePass = menu.keystorePass; }
iOS上:
static public void SetIosProject(Menu menu) { PlayerSettings.iOS.targetDevice = iOSTargetDevice.iPhoneAndiPad; if (isFormalPackage) { PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, ScriptingImplementation.IL2CPP); } else { PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, ScriptingImplementation.Mono2x); } }
3.icon方面的动态更改
1:ICON
这里放置美术给的app的icon
Android:
iOS上:
2:LoginImage 对应的图片是default
icon,图片都是从ICON文件夹里Android:256,iOS:180尺寸拷贝过来的。
public static void GenerateLoginImage(RuntimePlatform platform) { if (menu != null) { int appId = int.Parse(menu.ID); //D:\trunk1.0\BackUp\Plugins_Android\1 目录创建 string rootPath = Application.dataPath + "/BackUp/LoginImage/" + appId; rootPath = rootPath.Replace("Assets/", ""); DirectoryInfo rootDirec = null; if (File.Exists(rootPath) == false) //创建根目录 { rootDirec = Directory.CreateDirectory(rootPath); } else { rootDirec = new DirectoryInfo(rootPath); } Debug.Log("【LoginImage】:" + rootPath); string platformName = platform == RuntimePlatform.Android ? "Android" : "IOS"; string iconSizePic = platform == RuntimePlatform.Android ? "256.png" : "180.png"; string icon_srcPath = Application.dataPath + "/BackUp/ICON/" + appId + "/" + platformName + "/"+ iconSizePic; icon_srcPath = icon_srcPath.Replace("Assets/", ""); string icon_animPath = Application.dataPath + "/BackUp/LoginImage/" + appId+ "/Icon.png"; icon_animPath = icon_animPath.Replace("Assets/", ""); CopyAndReplace(icon_srcPath, icon_animPath);//app icon } } private static void CopyAndReplace(string srcPath, string animPath) { Debug.Log("从目录:" + srcPath); Debug.Log("拷贝到:" + animPath); File.Copy(srcPath, animPath, true); }
3:Share_Image
微信分享图片:图片按照你自己放置的路径来定
4:Plugins_Android
(稍后讲)
我们项目工程已经存在了icon引用了Assets下的某张图片,
当需要改动这些图片的时候,只需要将目标图片拷贝过来替换掉就可以。
static public void ReplaceIcon(int appId, RuntimePlatform platform) { string platformName = platform == RuntimePlatform.Android ? "Android" : "IOS"; string scrPath = Application.dataPath + "/BackUp/ICON/" + appId + "/" + platformName; scrPath = scrPath.Replace("Assets/", ""); string aminPath = Application.dataPath + "/Art/ICON/" + platformName; Debug.Log("--------------------------ReplaceIcon--------------------------------------"); Debug.Log("【ReplaceIcon scrPath】=" + scrPath); Debug.Log("【ReplaceIcon aminPath】=" + aminPath); FileUtils.CopyDir(scrPath, aminPath); }
so easy,就是简单的文件或文件夹拷贝
SDK相关
我们游戏接的是微信SDK,这里只讲微信SDK相关
1:Android
观察这个Plugins下的目录
bin文件夹放置jar包
res:icon之类
AndroidManifest.xml:项目的权限配置等
jar包
需要更改包名
如图所示:直接手动更改,导出csmj.jar
微信appid是动态改变的,所以把appid抽出到配置AndroidManifest.xml
然后代码里动态获得:
public String getWXAppId() { ApplicationInfo appInfo; String WXAppId = ""; try { PackageManager pm = this.getPackageManager(); if(pm != null) { appInfo = pm.getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA); WXAppId =appInfo.metaData.getString("WXAppId"); Log.i("Unity", "WXAppId="+WXAppId); } }catch(NameNotFoundException e) { e.printStackTrace(); Log.d("Unity", e.getMessage()); } return WXAppId; }
res:
从ICON对应拷贝
//修改Plugins/Android/Res public static void RefreshResFolder() { if (menu != null) { int appId = int.Parse(menu.ID); //D:\trunk1.0\BackUp\Plugins_Android\1 目录创建 string rootPath = Application.dataPath + "/BackUp/Plugins_Android/" + appId; rootPath = rootPath.Replace("Assets/", ""); DirectoryInfo rootDirec = null; if (File.Exists(rootPath) == false) //创建根目录 { rootDirec = Directory.CreateDirectory(rootPath); } else { rootDirec = new DirectoryInfo(rootPath); } Debug.Log("目标根目录:" + rootPath); //D:\trunk1.0\BackUp\Plugins_Android\0 将模板目录下的res文件夹拷贝到目标目录下 string templateSrcPath = Application.dataPath + "/BackUp/Plugins_Android/0"; templateSrcPath = templateSrcPath.Replace("Assets/", ""); FileUtils.CopyDir(templateSrcPath, rootPath);//把0目录下的内容拷贝到目标目录 string[] folders = new string[] { "drawable", "drawable-hdpi", "drawable-ldpi", "drawable-mdpi", "drawable-xhdpi", "drawable-xxhdpi", "drawable-xxxhdpi" }; string[] icons = new string[] { "96", "72", "36", "48", "96", "144", "192" }; string[] newName_icons = new string[] { "icon", "app_icon", "app_icon", "app_icon", "app_icon", "app_icon", "app_icon" }; int folderLen = folders.Length; for (int folderIndex = 0; folderIndex < folderLen; folderIndex++) { string platformName = "Android"; string icon_srcPath = Application.dataPath + "/BackUp/ICON/" + appId + "/" + platformName+"/"+icons[folderIndex]+".png";//D:\trunk1.0\Assets\BackUp\ICON\9\Android\96.png icon_srcPath = icon_srcPath.Replace("Assets/", ""); string icon_animPath = rootPath + "/res/"+ folders[folderIndex]+"/" + newName_icons[folderIndex] + ".png"; CopyAndReplace(icon_srcPath, icon_animPath); } } }
AndroidManifest.xml
修改要点:替换成新包名,wxappid替换,urlscheme替换
static public void ChangeAndroidXml(int appId, Menu menu) { Debug.Log("--------------------------ChangeAndroidXml--------------------------------------"); string xmlPath = Application.dataPath + "/Plugins/Android/AndroidManifest.xml"; XmlDocument xmlDoc = new XmlDocument(); XmlReaderSettings set = new XmlReaderSettings(); set.IgnoreComments = true;//这个设置是忽略xml注释文档的影响。有时候注释会影响到xml的读取 XmlReader reader = XmlReader.Create(xmlPath, set); xmlDoc.Load(reader); reader.Close();//最后读取完毕后,记得要关掉reader. //获取bookshop节点的所有子节点 XmlNodeList node = xmlDoc.GetElementsByTagName("manifest"); string oldPackageName = ""; foreach (XmlElement n in node) { if (n.Name == "manifest") { string package = n.Attributes["package"].Value; if (package != "") { Debug.Log("【 旧包名】:" + package); oldPackageName = package; n.SetAttribute("package", menu.bundle_identifier); } break; } } XmlNodeList nodeList = xmlDoc.SelectSingleNode("manifest").ChildNodes; foreach (XmlElement xn in nodeList) { //Debug.Log("name =" + xn.Name); if (xn.Name == "application") { XmlNodeList target = xn.ChildNodes; foreach (XmlElement xx in target) { if (xx.Name == "meta-data")//替换微信APPID { ChangeWXAppId(menu, xx); } else if (xx.Name == "activity")//替换APP的scheme { ChangeAppScheme(menu, xx); if (oldPackageName != "") { XmlAttributeCollection attributeCollection = xx.Attributes; foreach (XmlAttribute attr in attributeCollection) { //Debug.Log("attr attr.Name" + attr.Name + "---attr.Value=" + attr.Value); if (attr.Value.Contains(oldPackageName)) { //替换掉旧包名 string value = attr.Value.Replace(oldPackageName, menu.bundle_identifier); xx.SetAttribute(attr.Name, value); } } } } } } } xmlDoc.Save(xmlPath); Debug.Log("-----------------------修改 Androidmanifest OK---------------------------"); } private static void ChangeAppScheme(Menu menu, XmlElement ele) { Debug.Log("------------------------------------ChangeAppScheme------------------------------"); XmlNodeList activityNodes = ele.ChildNodes; foreach (XmlElement xmlNode in activityNodes) { if (xmlNode.Name == "intent-filter") { XmlNodeList intentNodes = xmlNode.ChildNodes; foreach (XmlElement node in intentNodes) { // <data android:scheme="csmj" /> if (node.Name =="data") { if(node.GetAttribute("android:scheme") != "") { node.SetAttribute("android:scheme", menu.scheme); Debug.Log("【App scheme】:" + menu.scheme); return; } } } } } } private static void ChangeWXAppId(Menu menu, XmlElement ele) { Debug.Log("------------------------------------替换微信APPID------------------------------"); // <meta-data android:name="WXAppId" android:value="test" /> if (ele.GetAttribute("android:name") == "WXAppId") { ele.SetAttribute("android:value", menu.WXAppID); Debug.Log("【微信APPID 】=" + menu.WXAppID); } }
至此,Android的修改已经完成。
2.iOS
using System.Collections;using System.Collections.Generic;using System.IO;using UnityEditor;using UnityEditor.Callbacks;#if UNITY_IPHONEusing UnityEditor.iOS.Xcode;#endifusing UnityEngine;#if UNITY_IPHONEpublic class XcodeSetting : MonoBehaviour{ private static List<Menu> menuList; [PostProcessBuild(999)] public static void OnPostprocessBuild(BuildTarget BuildTarget, string path) { if (BuildTarget == BuildTarget.iOS) { Debug.Log(path); string projPath = PBXProject.GetPBXProjectPath(path); PBXProject proj = new PBXProject(); proj.ReadFromString(File.ReadAllText(projPath)); string target = proj.TargetGuidByName(PBXProject.GetUnityTargetName()); proj.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); //C#读取TXT文件之建立 FileStream 的对象,说白了告诉程序, //文件在那里,对文件如何 处理,对文件内容采取的处理方式 string txtPath = Application.dataPath + "/Resources/AppId.txt"; FileStream fs = new FileStream(txtPath, FileMode.Open, FileAccess.Read); StreamReader sr = new StreamReader(fs);//仅 对文本 执行 读写操作 int AppId = int.Parse(sr.ReadToEnd()); //C#读取TXT文件之关上文件,留心顺序,先对文件内部执行 关上,然后才是文件~ sr.Close(); fs.Close(); menuList = ExcelAccess.SelectMenuTable(1, AppId); if (menuList != null) { if (menuList.Count > 0) { Menu menu = menuList[0]; string wxAppid = menu.WXAppID; //UrlType //Handle plist string plistPath = path + "/Info.plist"; PlistDocument plist = new PlistDocument(); plist.ReadFromString(File.ReadAllText(plistPath)); PlistElementDict rootDict = plist.root; rootDict.SetString("NSLocationAlwaysUsageDescription", "地理位置ios8后"); rootDict.SetString("NSLocationUsageDescription", "iOS8之前向用户申请位置信息获取授权"); rootDict.SetString("NSLocationWhenInUseUsageDescription", "地理位置iOS8后"); rootDict.SetString("NSMicrophoneUsageDescription", "使用麦克风"); rootDict.SetString("NSPhotoLibraryUsageDescription", "使用相册"); PlistElementArray urlTypes = rootDict.CreateArray("CFBundleURLTypes"); // add weixin url scheme PlistElementDict wxUrl = urlTypes.AddDict(); wxUrl.SetString("CFBundleTypeRole", "Editor"); wxUrl.SetString("CFBundleURLName", "weixin"); PlistElementArray wxUrlScheme = wxUrl.CreateArray("CFBundleURLSchemes"); wxUrlScheme.AddString(wxAppid); //add csmj url scheme PlistElementDict appUrl = urlTypes.AddDict(); appUrl.SetString("CFBundleTypeRole", "Editor"); appUrl.SetString("CFBundleURLName", "chaoshanmajiang"); PlistElementArray appUrlScheme = appUrl.CreateArray("CFBundleURLSchemes"); appUrlScheme.AddString(menu.scheme); File.WriteAllText(plistPath, plist.WriteToString()); } } }}}#endif
iOS修改xcode工程,需要在打包完毕后,会自动调用
[PostProcessBuild(999)]
public static void OnPostprocessBuild(BuildTarget BuildTarget, string path)
如果代码报错,请在playersetting里 下载ios相关。
其他设置等我以后解锁再添加。Mark一下。
2017-08-07更新
PS:新增替换外部图片
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
if UNITY_IPHONE
using UnityEditor.iOS.Xcode;
endif
using UnityEngine;
if UNITY_IPHONE
public class XcodeSetting : MonoBehaviour
{
private static List
[PostProcessBuild(999)]public static void OnPostprocessBuild(BuildTarget BuildTarget, string path){ if (BuildTarget == BuildTarget.iOS) { Debug.Log("OnPostprocessBuild ProjectPath:" + path); string projPath = PBXProject.GetPBXProjectPath(path);//获取.xcodeproj文件的路径 PBXProject proj = new PBXProject();//new()一个PBXProject对象,然后从上面获取的路径中读出字符串。 string contents = File.ReadAllText(projPath); proj.ReadFromString(contents); string target = proj.TargetGuidByName(PBXProject.GetUnityTargetName());//获取targetGUID // 链接器 proj.SetBuildProperty(target, "ENABLE_BITCODE", "NO");//bitcode是被编译程序的一种中间形式的代码。包含bitcode配置的程序将会在App store上被编译和链接。bitcode允许苹果在后期重新优化我们程序的二进制文件(我们第三方库不一定支持,所以要设置为NO) proj.SetBuildProperty (target, "OTHER_LDFLAGS", "-Objc -all_load -lstdc++.6.0.9 -lsqlite3");//Other Linker Flags 在ios开发过程中,有时候会用到第三方的静态库(.a文件),然后导入后发现编译正常但运行时会出现selector not recognized的错误,从而导致app闪退。 //pbxProj.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**"); //pbxProj.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**"); //pbxProj.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)/Libraries/BabyFrameWork/**"); //------------------------拷贝系统Framework---------------------------------------------- string xcodePath = Application.dataPath + "/xcode.txt"; xcodePath = xcodePath.Replace("Assets/", ""); FileStream xcode_fs = new FileStream(xcodePath, FileMode.Open, FileAccess.Read); StreamReader xcode_sr = new StreamReader(xcode_fs);//仅 对文本 执行 读写操作 string line = null; while((line= xcode_sr.ReadLine())!= null) { Debug.Log ("framework=" + line); string frameWorkName = line.Split('.')[0]; string filterName = line.Split ('.') [1]; if (filterName == "framework") { if(frameWorkName == "JavaScriptCore") { proj.AddFrameworkToProject(target, line, true);//这里第一个参数是第三部中获取到的GUID,第二个参数是framework名(这里一定要是.framework为后缀),第三个参数是用来设置framework是require还是optional。 } else { proj.AddFrameworkToProject(target, line, false);//这里第一个参数是第三部中获取到的GUID,第二个参数是framework名(这里一定要是.framework为后缀),第三个参数是用来设置framework是require还是optional。 } } else { proj.AddFileToBuild (target, proj.AddFile("usr/lib/"+line, "Frameworks/"+line, PBXSourceTree.Sdk)); } } //C#读取TXT文件之关上文件,留心顺序,先对文件内部执行 关上,然后才是文件~ xcode_sr.Close(); xcode_fs.Close(); //--------------------------拷贝系统Framework end------------------------------------- File.WriteAllText(projPath, proj.WriteToString()); //------------------------------APPID----------------------------------------------------- string txtPath = Application.dataPath + "/Resources/AppId.txt"; FileStream fs = new FileStream(txtPath, FileMode.Open, FileAccess.Read); StreamReader sr = new StreamReader(fs);//仅 对文本 执行 读写操作 int AppId = int.Parse(sr.ReadToEnd()); //C#读取TXT文件之关上文件,留心顺序,先对文件内部执行 关上,然后才是文件~ sr.Close(); fs.Close(); menuList = ExcelAccess.SelectMenuTable(1, AppId); if (menuList != null) { if (menuList.Count > 0) { Menu menu = menuList[0]; string wxAppid = menu.WXAppID; PlistElementDict dict; //UrlType //Handle plist string plistPath = path + "/Info.plist"; PlistDocument plist = new PlistDocument(); plist.ReadFromString(File.ReadAllText(plistPath)); PlistElementDict rootDict = plist.root; //NSContactsUsageDescription->通讯录 //NSMicrophoneUsageDescription->麦克风 //NSPhotoLibraryUsageDescription->相册 //NSCameraUsageDescription->相机 //NSLocationAlwaysUsageDescription->地理位置 //NSLocationWhenInUseUsageDescription->地理位置 rootDict.SetString("NSLocationAlwaysUsageDescription", "地理位置相近的玩家不可进入同一个牌桌"); rootDict.SetString("NSLocationUsageDescription", "地理位置相近的玩家不可进入同一个牌桌"); rootDict.SetString("NSLocationWhenInUseUsageDescription", "地理位置相近的玩家不可进入同一个牌桌"); rootDict.SetString("NSMicrophoneUsageDescription", "使用麦克风"); rootDict.SetString("NSPhotoLibraryUsageDescription", "使用相册"); //PList文件添加微信为白名单 PlistElementArray array = rootDict.CreateArray("LSApplicationQueriesSchemes"); array.AddString("weixin"); // 设置支持HTTP dict = rootDict.CreateDict("NSAppTransportSecurity"); dict.SetBoolean("NSAllowsArbitraryLoads", true); PlistElementArray urlTypes = rootDict.CreateArray("CFBundleURLTypes"); // add weixin url scheme应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。 PlistElementDict wxUrl = urlTypes.AddDict(); wxUrl.SetString("CFBundleTypeRole", "Editor"); wxUrl.SetString("CFBundleURLName", "weixin"); PlistElementArray wxUrlScheme = wxUrl.CreateArray("CFBundleURLSchemes"); wxUrlScheme.AddString(wxAppid); //add csmj url scheme PlistElementDict appUrl = urlTypes.AddDict(); appUrl.SetString("CFBundleTypeRole", "Editor"); appUrl.SetString("CFBundleURLName", "chaoshanmajiang"); PlistElementArray appUrlScheme = appUrl.CreateArray("CFBundleURLSchemes"); appUrlScheme.AddString(menu.scheme); //add location appkey url scheme PlistElementDict locationUrl = urlTypes.AddDict(); locationUrl.SetString("CFBundleTypeRole", "Editor"); locationUrl.SetString("CFBundleURLName", "locationAppKey"); PlistElementArray locationScheme = locationUrl.CreateArray("CFBundleURLSchemes"); locationScheme.AddString("xx"+menu.Location_AppKey); File.WriteAllText(plistPath, plist.WriteToString()); } } CopyAndReplaceIcon (path); }}static void CopyAndReplaceIcon (string projectPath){ string targetWXShareIconPath = projectPath + "/SDK/WX/res2.png"; string sourceIconPath = System.Environment.CurrentDirectory + "/Assets/Art/ICON/IOS/57.png"; Debug.Log (string.Format ("CopyAndReplaceIcon from {0} to {1}", sourceIconPath, targetWXShareIconPath)); File.Copy (sourceIconPath, targetWXShareIconPath, true);}
}
endif
- Unity-android/iOS自动设置ProjectSetting
- 【Unity】—iOS 打包自动设置
- unity ios 打包设置
- Jenkins + Unity + IOS 构建IOS自动编译
- Unity导入Texture自动设置参数
- unity自动设置Assetbundle包名
- Unity iOS 自动修改Xcode配置
- Unity打包IOS自动发布fir.im
- Unity 设置Android SDK path
- android 自动设置mtu
- Unity 用户手册iOS 开发入门账户设置
- Jenkins + Unity + Android构建基于Unity的自动编译环境
- [Unity][Android]Unity与Android设置USB交互
- visual studio自动补全unity组件拼写设置
- 设置unity 编译文件到android项目
- unity关于android打包性能设置
- android 设置开机自动启动
- [Useful] android自动时间设置
- 继承 Objective-C中public、protected、private的使用
- iOS Xcode 编译错误 “expression is not assignable”
- CentOS增加swap分区大小
- 查看已安装的控件
- 111
- Unity-android/iOS自动设置ProjectSetting
- 文章标题
- linux驱动增加work工作队列和获取唤醒锁操作
- IOS通知中心(观察者模式)[NSNotificationCenter defaultCenter]
- 自己实现Spring AOP(三)CGLib代理实现AOP
- Android studio相关问题
- C++ primer plus 阅读记录-类和动态内存分配
- JAVA加密算法- 对称加密算法二
- Android 7.0 Gallery图库源码分析9