回首一下,故事的背景……
我們依稀記得,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.csdn.net/jaywon/article/details/8208991
更多關於用cocos2d編寫《憤怒的小鳥》遊戲的筆記請看 《iOS cocos2d學習筆記》系列
扉頁:http://blog.csdn.net/youngsblog/article/details/9717335