Unity & IOS
准备工作
设备
苹果电脑安装Xcode,苹果手机或者ipad
Unity
安装IOS模块
其他
证书:development证书或者distribute证书和对应描述文件(.mobileprovision)
打包苹果流程
注意:不要有中文路径!!!
我使用的是windows电脑发布工程,然后使用Mac电脑发布,为了方便,我在window磁盘添加了一个共享文件夹,使用Mac访问共享文件夹,Xcode直接打开工程,这样就不用传来传去了. 当然也可以直接使用MacUnity发布
- Win中Unity中发布到共享文件夹
- Mac中XCode直接打开共享文件夹中工程
Unity中发布工程
设置Icon,最好用一个1024*1024的图,直接设置为DefaultIcon
就行了
设置Bundle Identifier,一定要和证书保持一致
(也就是和你苹果后台app包名一致)
然后直接Build就可以了,打出的是一个Xcode工程,要求发布的文件夹是一个空文件,所以重复打包同一个文件夹,最好删除原来的或者把里面的文件都删了
XCode打包
一定要注意磁盘空间够用,不然有很多问题,确保有10G以上
我使用的windows系统build xcode工程到一个共享文件夹,然后mac通过局域网直接访问windows 里面的工程
可以使用自动签名登录一下开发者账号,也可以使用证书配置文件
点击Provisioning Profile
,选择对应配置文件就可以
然后点击Product,如果想直接跑在手机上,点击run,如果需要发布ipa或者上传App StoreConnect就点击Archive
App Store Connetct :导出的ipa 包可以发布到App Store或者在越狱的iOS设备。
Ad Hoc :安装测试用的,有udid限制。该ipa包不能提交到AppStore.
Enterprise :导出的ipa 包是用于企业应用账号的,个人账号是无法选择的。而且没有任何udid设备数量限制。
Development :导出的ipa包是dev证书编译的,其实这个和第二Ad Hoc很类似,唯一区别这个用dev证书编译的,而Ad Hoc用的是dis证书编译的。
最后导出到目标文件夹就可以了
然后就可以安装了
安装方式
- 第一方式是直接手机连接上Mac,XCode->Wondow->Diveces and Simulators
- 第二种通过
蒲公英平台
上传下载,不会的自行搜索
修改代码重新打包
unity重新build如果又重新把项目拷贝到mac,那么之前的设置都没了很麻烦, 大部分我们只要把新生成的copy,然后覆盖Classes
Data
Library
文件夹
Classes
是程序文件(il2cpp,生成的cpp)
Data
跟其他差不多
Library
是一些插件包 一般也不会修改这个
Unity与IOS交互
扩展名 | 说明 |
---|---|
.h | 头文件,它包含类名,类继承的父类,还有方法和变量的声明。它定义的类的成员变量以及方法等等是公开的,外部是可以访问的。 |
.m | 是纯Object-C 文件(.m只能调用纯Object-C的类,不能调用混合的). 实现文件,可以包含Objective-C和C代码。同时,它是对.h文件中方法的实现外部不能访问, |
.mm | 是Object-C和C++混合(.mm可以调用Object-C的,也可以调用C++的). 实现文件,和.m文件类似,唯一的不同点就是,除了可以包含Objective-C和C代码以外,还可以包含C++代码。仅在你的Objective-C代码中确实需要使用C++类或者特性的时候才用这种扩展名。 |
Objective-c中.m、.h、.mm文件 - 何人之名 - 博客园 (cnblogs.com)
C#可以调用C和C++代码,而C和C++可以调用Objective-C代码,这里我们将.m后缀改为.mm,这样,就可以在里面编写C和C++代码.
Unity做插件就是使用.mm文件
,调用的都是c和c++代码
什么是extern "C"
extern “C” 是C++ 指令,是告诉编译器 ,这里面的代码用C规则来编译
Unity调用IOS
Unity 中是直接调用IOS声明的方法,不用找到类
1 | //固定写法 |
OC代码基本都是面向对象的,即需要new一个对象才能调用,但是C不用,Unity直接调用C代码,然后再对应代码 newOC对象.
调用代码中写的方法,需要使用符合C代码写法.
IOS调用Unity
UnitySendMessage(const char *obj, const char *method, const char *msg)
第一个参数是物体名字 第二个参数是物体上面的方法(private也可以) 第三个参数
示例(使用Scheme url 打开其他app)
- 在xcode中声明一个.h文件和.mm文件
1 |
|
1 |
|
将两个文件导入Unity->Plugins->IOS目录
在Unity脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23using System.Collections;
using System.Runtime.InteropServices;
/// <summary>
/// ____DESC: Unity IOS 交互
/// </summary>
public class Native_IOS
{
//固定写法
[ ]
//在mm或者.h中声明的方法名字 必须保持一致
//c# extern 表示这个方法由外部实现,简单说这个方法是外部某个文件某个dll实现的 自己不需要实现
/// <summary>
/// 调用IOS args用 "|"分割
/// </summary>
private static extern void UnityCallIOS(string key,string args);
public void UnityToIOS(string key, string[] args)
{
var v = string.Join('|', args);
UnityCallIOS(key, v);
}
}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
66using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// ____DESC: 与原生平台交互管理器
/// </summary>
public class NativeManager : MonoSingleton<NativeManager>, IManager
{
public event System.Action<RuntimePlatform, string[]> OnReceiveNativeMsg;
private Native_IOS native_IOS;
public bool IsInit { get; set; } = false;
private RuntimePlatform currRuntimePlatform;
public void Init()
{
this.gameObject.name = nameof(NativeManager);
currRuntimePlatform = Application.platform;
else if (currRuntimePlatform == RuntimePlatform.IPhonePlayer)
native_IOS = new Native_IOS();
SetDontDestoryOnload();
IsInit = true;
}
/// <summary>
/// 接收到其他平台发来的消息,可以是私有方法
/// </summary>
/// <param name="msg">msg包含一个|</param>
private void NativeToUnity(string msg)
{
//用|切分 第0个是平台
var msgs = msg.Split('|');
if (System.Enum.TryParse<RuntimePlatform>(msgs[0], ignoreCase: true, out RuntimePlatform runtimePlatform))
{
//其他参数
List<string> args = new List<string>();
for (int i = 1; i < msgs.Length; i++)
args.Add(msgs[i]);
OnReceiveNativeMsg?.Invoke(runtimePlatform, args.ToArray());
}
else
MyLog.LogError($"平台错误:{msgs[0]}");
}
/// <summary>
/// Unity调用原生平台
/// </summary>
/// <param name="runtimePlatform">平台</param>
/// <param name="key">key</param>
/// <param name="args">参数</param>
public void UnityCallNative(RuntimePlatform runtimePlatform, string key, params string[] args)
{
switch (runtimePlatform)
{
case RuntimePlatform.IPhonePlayer:
if (currRuntimePlatform == RuntimePlatform.IPhonePlayer)
{
native_IOS.UnityToIOS(key,args);
}
break;
}
}
}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
72using UnityEngine;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
public Button btnQQ, btnWeibo, btnLofter, btnBilibili, btnNone;
//QQ
string schemeQQGroup = "mqqapi://card/show_pslcard?src_type=internal&version=1&card_type=group&uin=1020819114";
string schemeQQGroup2 = "tim://card/show_pslcard?src_type=internal&version=1&uin=1020819114";
//Lofter
string schemeLofter = "lofter://qiyunyimeng.lofter.com/";
string httpLofter = "https://qiyunyimeng.lofter.com/";
//B站
string schemeBilibili = "bilibili://space/1346329729";
string httpBilibili = "https://m.bilibili.com/space/1346329729";
//抖音
string schemeDouyin = "https://v.douyin.com/2XR7GQ2/";
//微博
string schemeWeibo = "sinaweibo://userinfo?uid=5904919311";
string schemeWeibo2 = "weibointernational://userinfo?uid=5904919311";
string httpWeibo = "https://weibo.com/u/5904919311";
private void Start()
{
NativeManager.Ins.Init();
NativeManager.Ins.OnReceiveNativeMsg += Ins_OnReceiveNativeMsg;
btnQQ.onClick.AddListener(() => NativeManager.Ins.UnityCallNative(RuntimePlatform.IPhonePlayer, "OpenScheme", schemeQQGroup,"QQ"));
btnWeibo.onClick.AddListener(() => NativeManager.Ins.UnityCallNative(RuntimePlatform.IPhonePlayer, "OpenScheme", schemeWeibo2,"weibo"));
btnLofter.onClick.AddListener(() => NativeManager.Ins.UnityCallNative(RuntimePlatform.IPhonePlayer, "OpenScheme", schemeLofter,"lofter"));
btnBilibili.onClick.AddListener(() => NativeManager.Ins.UnityCallNative(RuntimePlatform.IPhonePlayer, "OpenScheme", schemeBilibili, "bilibili"));
btnNone.onClick.AddListener(() => NativeManager.Ins.UnityCallNative(RuntimePlatform.IPhonePlayer, "OpenScheme", "tt://sdfsdfdss","tt"));
}
//接收到消息
private void Ins_OnReceiveNativeMsg(RuntimePlatform platform, string[] args)
{
Debug.Log("接收到消息:" + platform.ToString() + " " + args[0]);
if (platform == RuntimePlatform.IPhonePlayer)
{
//提示没有安装该app
if (args[0] == "noapp")
{
//如果是qq
if (args[1] == "QQ")
{
}
//如果没有微博 就直接打开微博uri
else if (args[1] == "weibo")
{
Application.OpenURL(httpWeibo);
}
else if (args[1] == "lofter")
{
Application.OpenURL(httpWeibo);
}
else if (args[1] == "bilibili")
{
Application.OpenURL(httpBilibili);
}
else if (args[1] == "tt")
{
Application.OpenURL("http://www.baidu.com");
}
}
}
}
}注意点
IOS使用scheme的时候需要添加白名单,不然打不开.
添加scheme的时候不用加
://
第一种添加方式
第二种添加方式
参考链接
问题合集
证书不受信任
如果是别人给的证书那么就是以.p12
结尾,双击就可以导入,然后再钥匙串中就可以看到
如果证书显示不受信任
那么需要重新下载一个 证书
地址:Apple PKI - Apple 下载之后双击按照就可以
Distribution APP过程出现问题
出现The data couldn’t be read because it isn’t in the correct format
或者zip faild
什么的,都先检查是否存贮空间不够了
打出的IPA文件名是ProductName
ProductName 就是BundleName 游戏名字是Bundle Diaplay Name
在Unity中设置了Productname 如果是英文名 好像不会出现这个错误,如果是中文,那么就有可能出现这个名字
- 可以在Xcode工程中改
- 也可以在Unity导出的时候改
Info.plist
文件
这个时候我们可以在打包出IOS的时候自动设置一下,在unity 新建一个Editor脚本,加上代码
1 | [ ] |
#Unity# 在编辑器扩展中用PostProcessBuildAttribute来修改XCode工程的Product Name (xmanyou.com)
使用www(UnityWebRequest)无法读取SteamingAssets中的文件
Application.streamingAssetsPath
返回的位置因平台而异:
- 大多数平台(Unity Editor、Windows、Linux 播放器、PS4、Xbox One、Switch)使用
Application.dataPath + "/StreamingAssets"
。- macOS 播放器使用
Application.dataPath + "/Resources/Data/StreamingAssets"
。- iOS 使用
Application.dataPath + "/Raw"
。- Android 使用经过压缩的 APK/JAR 文件中的文件:
"jar:file://" + Application.dataPath + "!/assets"
。要在无法直接访问流媒体资源文件的平台(如 Android 和 WebGL)上读取流媒体资源,请使用 UnityWebRequest。有关示例,请参阅 Application.streamingAssetsPath。
在许多平台上,流媒体资源文件夹位置是只读的;您不能在运行时在这些位置修改或写入新文件。请使用 Application.persistentDataPath 来获取可写的文件夹位置。
即使有些平台可以用IO在SteamingAssets进行写入操作,但是最好不要这么做
IOS与Android不同,IOS需要在前面加一个 file://
Android是经过压缩的 所以不能直接IO读取 要使用www 加上"jar:file://"协议
用WWW类加载本地,要注意各个平台路径需要加的访问名称,例如Android平台的路径前要加"jar:file://",其他平台使用"file://"
Unity安卓、iOS、PC、Mac读写目录 - 简书 (jianshu.com)
The App Store Icon in the asset catalog in ‘qyym. app’ can’t be transparent nor contain an alpha channel
这个报错原因是图标有透明或者有透明通道
方法一:
直接把unity icon 图标搞成jpg然后再打包
方法二:
把每个图片用windows自带画图编辑保存(画图保存自然会丢失a通道)
The bundle at ‘my.app/Frameworks/UnityFramework.framework’ contains disallowed file ‘Frameworks’.
就是不能嵌套包含,这个问题不知出在哪,我自己打包的时候没啥问题,用火星人工具打包出现了问题
新的解决方案:将ks广告设置为动态库
最后点击Add Files重新添加相应的Framework
解决方案
- XCode在上传testflight前会先Archive,首先先在Achieve成功后的项目中右键Show in Finder
- 在.xcarchive文件点右键显示包内容
- 在Products/Applications/xx.app文件再点右键显示包内容
- 删除Frameworks/UnityFramework.framework/Frameworks文件夹
- 再重新上传testflight就可以成功上传了!
以上这些步骤可以用shell命令来处理,步骤如下:
在Build Phases下新增Run Script,并添加以下代码:
1 | cd |
转自:https://www.weixiuzhan.cn/news/show-29533.html
Command Ld failed with a nonzero exit code
这个问题多种多样,我这里记录一下
1:是我用mac连接window共享文件夹问题(很奇怪之前没这个问题)
DerivedData/Unity-iPhone-fazbdskwtnuvvqcdjsitsmtzxowo/Build/Products/ReleaseForRunning-iphoneos/UnityFramework.framework/UnityFramework’ does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.
在xcode中 build setting->bitcode 设置为NO
添加动态库设置
例如添加AVPRO