对cocos2d iphone的朝向处理的改进
我用cocos2d iphone作为我的iPhone/iPad游戏已经有一阵子了。cocos2d iphone是个经过考验的、方便使用的引擎,而且让你几乎可以很快地进行gameplay方面的开发。最近它加入了iPad的支持。然而,Apple对于iPad游戏的审批似乎比iPhone更严格了一些。因为iPad Human Interface Guideline中对于朝向敏感方面,对开发者做出了更高的要求。
鉴于以上原因,我准备让我的游戏变得对朝向改变更友好一点。在cocos2d iphone中,朝向的改变可以用注册UIDeviceOrientationDidChangeNotification事件后,再调用一些CCDirector的方法予以解决:
代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Enable orientation detection
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
// Register orientation detection
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(orientationDidChanged:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];
......
}
-(void) orientationDidChanged:(NSNotification*)notification
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[[CCDirector sharedDirector] setDeviceOrientation:(ccDeviceOrientation)orientation];
}
{
// Enable orientation detection
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
// Register orientation detection
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(orientationDidChanged:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];
......
}
-(void) orientationDidChanged:(NSNotification*)notification
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[[CCDirector sharedDirector] setDeviceOrientation:(ccDeviceOrientation)orientation];
}
然而,这个朝向的改变是很生硬的,没有任何切换动画。在做了一些功课之后,我决定自己写点代码搞定它。其实cocos2d中的朝向处理代码也没做什么事,无非就是调了一些必要的OS函数和坐标系转换。
所以我是这么做的:把CCDirector.applyLandscape里的所有gl调用都替换为CGAffineTransform的调用,而CGAffineTransform是可以根据时间插值的。最后把CGAffineTransform的矩阵转换为gl的矩阵:
代码
CGAffineTransform CGAffineTransformInterpolate(const CGAffineTransform *t0, const CGAffineTransform *t1, float factor)
{
// clamp factor to [0, 1]
if ( factor > 1 )
factor = 1;
if ( factor < 0 )
factor = 0;
return CGAffineTransformMake(t0->a*(1-factor) + t1->a*factor,
t0->b*(1-factor) + t1->b*factor,
t0->c*(1-factor) + t1->c*factor,
t0->d*(1-factor) + t1->d*factor,
t0->tx*(1-factor) + t1->tx*factor,
t0->ty*(1-factor) + t1->ty*factor);
}
// in your CCDirector.m:
- (void) setDeviceOrientation:(ccDeviceOrientation) orientation
{
if( deviceOrientation_ != orientation ) {
deviceOrientation_ = orientation;
targetTransform_ = CGAffineTransformIdentity;
elapsedSinceLastOrientationChange_ = 0;
CGSize s = [openGLView_ frame].size;
float w = s.width / 2;
float h = s.height / 2;
switch( deviceOrientation_) {
case CCDeviceOrientationPortrait:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO];
break;
case CCDeviceOrientationPortraitUpsideDown:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortraitUpsideDown animated:NO];
targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
targetTransform_ = CGAffineTransformRotate(targetTransform_, CC_DEGREES_TO_RADIANS(180));
targetTransform_ = CGAffineTransformTranslate(targetTransform_, -w, -h);
break;
case CCDeviceOrientationLandscapeLeft:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
targetTransform_ = CGAffineTransformRotate(targetTransform_, -CC_DEGREES_TO_RADIANS(90));
targetTransform_ = CGAffineTransformTranslate(targetTransform_, -h, -w);
break;
case CCDeviceOrientationLandscapeRight:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO];
targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
targetTransform_ = CGAffineTransformRotate(targetTransform_, CC_DEGREES_TO_RADIANS(90));
targetTransform_ = CGAffineTransformTranslate(targetTransform_, -h, -w);
break;
default:
NSLog(@"Director: Unknown device orientation");
break;
}
}
}
-(void) applyLandscape
{
static float m[16];
if ( elapsedSinceLastOrientationChange_ < 0.25f )
{
currentTransform_ = CGAffineTransformInterpolate(¤tTransform_, &targetTransform_,
elapsedSinceLastOrientationChange_ / 0.25f);
elapsedSinceLastOrientationChange_ += dt;
}
else
{
currentTransform_ = targetTransform_;
}
CGAffineToGL(¤tTransform_, m);
glMultMatrixf(m);
}
{
// clamp factor to [0, 1]
if ( factor > 1 )
factor = 1;
if ( factor < 0 )
factor = 0;
return CGAffineTransformMake(t0->a*(1-factor) + t1->a*factor,
t0->b*(1-factor) + t1->b*factor,
t0->c*(1-factor) + t1->c*factor,
t0->d*(1-factor) + t1->d*factor,
t0->tx*(1-factor) + t1->tx*factor,
t0->ty*(1-factor) + t1->ty*factor);
}
// in your CCDirector.m:
- (void) setDeviceOrientation:(ccDeviceOrientation) orientation
{
if( deviceOrientation_ != orientation ) {
deviceOrientation_ = orientation;
targetTransform_ = CGAffineTransformIdentity;
elapsedSinceLastOrientationChange_ = 0;
CGSize s = [openGLView_ frame].size;
float w = s.width / 2;
float h = s.height / 2;
switch( deviceOrientation_) {
case CCDeviceOrientationPortrait:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO];
break;
case CCDeviceOrientationPortraitUpsideDown:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortraitUpsideDown animated:NO];
targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
targetTransform_ = CGAffineTransformRotate(targetTransform_, CC_DEGREES_TO_RADIANS(180));
targetTransform_ = CGAffineTransformTranslate(targetTransform_, -w, -h);
break;
case CCDeviceOrientationLandscapeLeft:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
targetTransform_ = CGAffineTransformRotate(targetTransform_, -CC_DEGREES_TO_RADIANS(90));
targetTransform_ = CGAffineTransformTranslate(targetTransform_, -h, -w);
break;
case CCDeviceOrientationLandscapeRight:
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO];
targetTransform_ = CGAffineTransformTranslate(targetTransform_, w, h);
targetTransform_ = CGAffineTransformRotate(targetTransform_, CC_DEGREES_TO_RADIANS(90));
targetTransform_ = CGAffineTransformTranslate(targetTransform_, -h, -w);
break;
default:
NSLog(@"Director: Unknown device orientation");
break;
}
}
}
-(void) applyLandscape
{
static float m[16];
if ( elapsedSinceLastOrientationChange_ < 0.25f )
{
currentTransform_ = CGAffineTransformInterpolate(¤tTransform_, &targetTransform_,
elapsedSinceLastOrientationChange_ / 0.25f);
elapsedSinceLastOrientationChange_ += dt;
}
else
{
currentTransform_ = targetTransform_;
}
CGAffineToGL(¤tTransform_, m);
glMultMatrixf(m);
}
现在,cocos2d的朝向处理就可以有漂亮的切换动画了。我把patch文件上传了,如果有兴趣的话可以下载看看。我是基于cocos2d iphone 0.99.0的代码做的。