回首一下,故事的背景……
我们依稀记得,iOS View与OpenGL View之间有着不可告人的秘密关系,
有人说,他们呢,就像是天上的牛郎星和织女星,他们的坐标原定被手机屏幕隔开在左边的两个角落。
iOS View的坐标原点位于手机屏幕的左上角,OpenGL View的则位于手机屏幕的左下角,无论凡人们如何将手机来个巴黎铁塔翻转再翻转,也没可能改变他们悲剧的命运——相隔屏幕两方,一个左上,一个左下。
但是,凡人都知道,牛郎织女却会在每年的那么几天走在一起……
等等骚年,你逗我玩吗?你的意思是说iOS View与OpenGL View的坐标原点走到了一起!?
我可以很认真,很老实地告诉你:
嗯,有情人终成眷属。
确实,这种看似不可能的事情让我碰上了
在写关于cocos2d坐标转换的一篇笔记当中,通过打印相关坐标作为验证的时候发现了这个问题,而且还困惑了我那么一段时间。
在我不懈的努力下,经过了实践和试验得出,牛郎和织女是可以走在一起的。
我的意思是:iOS View与OpenGL View的坐标原点是可以走在一起的。
好吧,在开始探讨之前,我必须告诉大家一件事,在这一个案例里面的情况,仅在iOS5以及iOS6之间测试过,只有iOS5或之前版本会出现这样问题,对于现在已经是iOS7侵略期,这算是就知识吧,有兴趣就往下看,没兴趣亦可了解一下。
我先贴遇到这样问题的工程中的一段关键代码:
AppDelegate.mm:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
if (![CCDirector setDirectorType:kCCDirectorTypeDisplayLink])
{
// 设置导演类型
[CCDirector setDirectorType:kCCDirectorTypeDefault];
}
CCDirector *director = [CCDirector sharedDirector];
// 取得当前导演
// 创建一个舞台
EAGLView *glView = [EAGLView viewWithFrame:[self.window bounds]pixelFormat:kEAGLColorFormatRGB565 depthFormat:0];
[director setOpenGLView:glView];// 设置OpenGLView
if ([[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
[director setDeviceOrientation:CCDeviceOrientationLandscapeLeft];
// 设置游戏方向为左横平
}
[director setAnimationInterval:1.0f/60.0f];
// 设置游戏刷新率为每秒60真
[director setDisplayFPS:YES];
//
RootViewController *rvc = [[RootViewController alloc] init];
[rvc setView:glView];
[self.window setRootViewController:rvc];
[rvc release];
// 把rvc作为window作为根视图
[self.window makeKeyAndVisible];
// 屏幕要显示第一个剧场
CCScene *sc = [LoadingScene scene];
[[CCDirector sharedDirector] runWithScene:sc];
// 让导演运行剧场
return YES;
}
哟!这不是appdelegate里面的启动方法嘛?
没错,眼利的童鞋一眼就可以看出来了,牛郎与织女之所以能走到一起,离不开起始的启动状态的设置。
我们先通读一下这个方法的实现过程,了解两人的作案动机(写得略乱,求大神勿喷,新手勿模仿)
不过,我还是没有突出最最核心的一句。
那这就来了:
if ([[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
[director setDeviceOrientation:CCDeviceOrientationLandscapeLeft];
// 设置游戏方向为左横平
}
是的,就是这一句话,让两个View的坐标原点走在一起的,正是因为这有条件的一句,让基情发生在iOS6以前的版本中。
我为什么要这样子写呢?
现在我们来了解一下两个因素:
1、iOS6.0的设备与视图的自动旋转非常简单,只需简单设置plist文件中相应的设置项“Supported interface orientations”即可支持旋转,但在之前版本则需要通过重写supportedInterfaceOrientations方法来实现,具体请看文章末尾
2、setDevicOrientation 这个方法很懒只负责将设备转向,而并没有将屏幕中的视图跟着一起旋转(这个方法在iOS6.0以前才需要执行来设置机械的横屏)但需要注意的是,重力感应依然有效,设备是真实旋转过的。
好,我们再来看一下最上面的代码,看看我做了什么事?
运行步骤:
1. 创建导演类
2. 以当前画面配置设置一个EAGLView对象glView(注意这不是我们讨论的那个OpenGL View),并把这个对象托付给导演(导演要播的动画都会在这个视图上面出现)
3. (iOS6.0以前版本)设备方向设为左横屏
4. 创建一个ViewController对象 rvc(这个就是iOS View),并将glView作为其视图。
5. 创建一个LoadingScene类剧场 sc(这个则是所讨论的OpenGL View),并使用导演将其运作起来。
再附上LoadingScene类在初始化时运作的代码:
+ (id) scene
{
CCScene *sc = [CCScene node];
// 创建空的剧场
LoadingScene *ls = [LoadingScene node];
// 创建自己的节目
[sc addChild:ls];
// 把节目加到通用剧场尚
return sc;
}
+ (id) node
{
return [[[[self class] alloc] init]autorelease];
}
// CCLayer.m
-(id) init
{
if( (self=[super init]) ) {
CGSize s = [[CCDirector sharedDirector] winSize];
anchorPoint_ = ccp(0.5f, 0.5f);
[self setContentSize:s];
self.isRelativeAnchorPoint = NO;
isTouchEnabled_ = NO;
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
isAccelerometerEnabled_ = NO;
#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
isMouseEnabled_ = NO;
isKeyboardEnabled_ = NO;
#endif
}
return self;
}
LoadingScene类继承于CCLayer类的,于是我们可以通过上面代码发现,LoadingScene对象在初始化过程中调用到CCLayer的init方法,而其中使用到了导演类的 winSize 方法,并以其作为setContentSize的参数。
其中winSize方法是获取当前屏幕CGRect(x,y,width,height)的方法。
那么,回到最上面的代码中,设备旋转过后创建的剧场sc的视图width = 480,height = 320,横放,坐标原点按照OpenGL View的规格放在左下角。
而在设备旋转之前、作为rvc视图的glView则是使用当时window的长和宽来定义的width =320,height = 480,竖直摆放(相对手机来说),未随着设备的旋转自动调整角度,而是跟着设备一起旋转。所以,坐标原点按照iOS View放在左上角,也就是设备正看时的左上角,横放时的左下角。
好了,说到这里,这个唯美的故事就这样发生了,而且已经成为一历史了,完。
关于iOS6的屏幕旋转详细解说请参考:http://blog.youkuaiyun.com/jaywon/article/details/8208991
更多关于用cocos2d编写《愤怒的小鸟》游戏的笔记请看 《iOS cocos2d学习笔记》系列
扉页:http://blog.youkuaiyun.com/youngsblog/article/details/9717335