起因
游戏测试过程中有这样一个反馈, 按住控制角色行走, 当按住触摸屏幕左铡边缘部位时, 有时会有1秒左右的延迟. 很是影响游戏的体验.多次测试后, 具体表现为
表现
1. 支持3DTouch的设备才有这个问题, iphone6s/iphone7, 其它设备没有这个问题2. 这些设备关闭3DTouch功能后, 问题就消失了
原因
所以问题就在于3DTouch上了. 15年10月, 苹果推出这项技术后, 其中一个使用小技巧就是在竖屏模式下. 任意界面在屏幕左侧边缘处使用3D Touch轻按后再次重按或向右滑动至屏幕中部即可激活多任务界面。
正是这个系统级的判定, 导致了这一游戏或应用中的问题.
解决
寄希望于用户去手动关闭这个功能, 不能算是个解决方式好的方式是: 取到窗口对象的UIGestureRecognizer, 设置它的delaysTouchesBegan为false
在Unity下具体操作方式为
在 Unity 插件目录下创建以下文件:
/path/to/unity/project/Assets/Plugins/iOS/MyAppController.h
/path/to/unity/project/Assets/Plugins/iOS/MyAppController.mm
注意,文件名必须是 ___AppController,前缀可自选,但不能省略;否则在 Build 项目的时候,会被移动到错误的目录中去。
MyAppController.h文件
#import "UnityAppController.h"
@interface MyAppController : UnityAppController
@end
IMPL_APP_CONTROLLER_SUBCLASS (MyAppController)
MyAppController.mm文件内容
#import "MyAppController.h"
@implementation MyAppController
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
- (void) preStartUnity
{
UIGestureRecognizer* gr0 = _window.gestureRecognizers[0];
gr0.delaysTouchesBegan = false;
UIGestureRecognizer* gr1 = _window.gestureRecognizers[1];
if(gr1 != NULL){
gr1.delaysTouchesBegan = false;
}
}
@end
在 Build iOS Project 的时候,Unity 会自动把MyAppController 复制到 /path/to/project/Libraries/MyAppController
而原来的 UnityAppController.mm 则在 /path/to/project/Classes/UnityAppController.mm
那么 Unity 是如何知道要使用我们定制的 MyAppController 而不是使用默认的 UnityAppController 呢?
关键在于 IMPL_APP_CONTROLLER_SUBCLASS 这个宏
很多文章在提到继承 UnityAppController 后,需要找到 /path/to/project/Classes/main.mm
里面的:
const char* AppControllerClassName = "UnityAppController";
将其修改为:
const char* AppControllerClassName = "MyAppController";
从而使 Unity 在启动的时候使用我们制定的MyAppController 类。
这样一来,每次 Build 项目都需要手动去修改这个常量,岂不是自找麻烦。其实完全可以利用 Objective-c 的特性来自动完成这个操作。
注意到 UnityAppController.h 里面有这样一个宏:
#define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) \
@interface ClassName(OverrideAppDelegate) \
{ \
} \
+(void)load; \
@end \
@implementation ClassName(OverrideAppDelegate) \
+(void)load \
{ \
extern const char* AppControllerClassName; \
AppControllerClassName = #ClassName; \
} \
@end
将这个宏加到 MyAppController.h 中,即可实现自动设置 AppControllerClassName :
IMPL_APP_CONTROLLER_SUBCLASS (MyAppController)
是不是很神奇呢!IMPL_APP_CONTROLLER_SUBCLASS 使用了两个 Objective-C 的特性,一是 category
,用来给已有的类扩展新的方法;二是 +(void)load
静态方法,它会在运行时 MyAppController 类被加载到内存中时触发,这个时间点比 int main()
函数还要早,所以能够提前“篡改” AppControllerClassName
,达到我们的目的。
完毕!