详解iOS游戏开发中Cocos2D的坐标位置关系
接触Cocos2D有段时间了,今天特意研究了下Cocos2D坐标系中各种位置关系,anchor属性,CCNode坐标和地图坐标转换。
先看一段代码:
-(id)init { //alwayscall"super"init //Applerecommendstore-assign"self"withthe"super"returnvalue if((self=[superinit])){ CCTMXTiledMap*gameWorld=[CCTMXTiledMaptiledMapWithTMXFile:@"PositionText.tmx"]; [selfaddChild:gameWorld]; CGSizewinSize=[[CCDirectorsharedDirector]winSize]; CCLOG(@"gameWorld.mapSize.width:%.2f gameWorld.mapSize.height:%.2f",gameWorld.mapSize.width,gameWorld.mapSize.height); CCLOG(@"gameWorld.tileSize.width:%.2f gameWorld.tileSize.height:%.2f",gameWorld.tileSize.width,gameWorld.tileSize.height); CCLOG(@"winSize.width:%.2f winSize.height:%.2f",winSize.width,winSize.height); CCSprite*pointSprite=[CCSpritespriteWithFile:@"point.png"]; [gameWorldaddChild:pointSpritez:2tag:1]; pointSprite.position=ccp(20,20); pointSprite.anchorPoint=ccp(0.5,0.5); CCSprite*rectSprite=[CCSpritespriteWithFile:@"myrect.png"]; [gameWorldaddChild:rectSpritez:1tag:1]; rectSprite.position=ccp(20,20); rectSprite.anchorPoint=ccp(0.5,0.5); } returnself; }
1、加载地图:
CCTMXTiledMap*gameWorld=[CCTMXTiledMaptiledMapWithTMXFile:@"PositionText.tmx"];
2、获取手机屏幕大小
CGSizewinSize=[[CCDirectorsharedDirector]winSize];
3、地图格子数:
gameWorld.mapSize(10,10)
4、地图格子大小:
gameWorld.tileSize (20,20)
5、整个地图大小:
gameWorld.mapSize*gameWorld.tileSize;
当然这里所说的是地图格子是个正方形,非正方形也容易啦。
6、anchor属性
1)添加一个精灵,这个精灵是个像素为1*1的红色图片,设置坐标为20,20,即在地图的第一个格子的右上角,设置anchorPoint为(0.5,0.5)
2)再添加一个精灵,这个精灵是个像素为20*20的蓝色图片,设置坐标为20,20,即在地图的第一个格子的右上角,同样设置anchorPoint为(0.5,0.5)
运行效果是矩形精灵的中心和在点精灵的位置,即矩形精灵的锚点为第一个格子的右上角(20,20)坐标处
去掉两个精灵的anchorPoint属性
运行效果和上面相同
设置rectSprite的anchorPoint为ccp(0,0)
运行效果是矩形精灵的左下角与点精灵重合。
设置rectSprite的anchorPoint为ccp(1,1)
运行效果是矩形精灵的右上角与点精灵重合。
同理设置ccp(0.5,1),ccp(1,0.5)等
由上面可以得出:
1)anchorPoint属性默认为ccp(0.5,0.5)
2)当设置(0,0)时是以左下角为锚点
3)当设置(1,1)时是以右上角角为锚点
4)由此可以自己推论到底该把锚点设置在哪里
Cocos2D使用的是OpenGL坐标系
OpenGL坐标系:原点在左下角,X轴向右,Y轴向上
IPhone屏幕坐标系:原点在左上角,X轴向右,Y轴向下
很简单的判断方法:由于Cocos2D的坐标系的原点在左下角。所以精灵内部坐标原点也是左下角,即当anchorPoint为(0,0)时即以左下角为锚点,为(1,1)时就以右上角为锚点,当然(0.5,0.5)就是以精灵中心为锚点了。由此可以推算出-2,-3.... 2,3.... 等值时精灵锚点坐标。
7、坐标转换
-(id)init { //alwayscall"super"init //Applerecommendstore-assign"self"withthe"super"returnvalue if((self=[superinit])){ CCTMXTiledMap*gameWorld=[CCTMXTiledMaptiledMapWithTMXFile:@"PositionText.tmx"]; [selfaddChild:gameWorld]; CGSizewinSize=[[CCDirectorsharedDirector]winSize]; CCLOG(@"gameWorld.mapSize.width:%.2f gameWorld.mapSize.height:%.2f",gameWorld.mapSize.width,gameWorld.mapSize.height); CCLOG(@"gameWorld.tileSize.width:%.2f gameWorld.tileSize.height:%.2f",gameWorld.tileSize.width,gameWorld.tileSize.height); CCLOG(@"winSize.width:%.2f winSize.height:%.2f",winSize.width,winSize.height); CCSprite*pointSprite=[CCSpritespriteWithFile:@"point.png"]; [gameWorldaddChild:pointSpritez:2tag:1]; pointSprite.position=ccp(20,20); //pointSprite.anchorPoint=ccp(0.5,0.5); CCSprite*rectSprite=[CCSpritespriteWithFile:@"myrect.png"]; [gameWorldaddChild:rectSpritez:1tag:1]; rectSprite.position=ccp(40,40); rectSprite.anchorPoint=ccp(2,2); [selfsetIsTouchEnabled:YES]; } returnself; } -(void)registerWithTouchDispatcher{ [[CCTouchDispatchersharedDispatcher]addTargetedDelegate:selfpriority:INT_MIN+1swallowsTouches:YES]; } -(BOOL)ccTouchBegan:(UITouch*)touchwithEvent:(UIEvent*)event{ CGPointpoint=[touchlocationInView:[touchview]]; CCLOG(@"ScreencoordX:%.2fcoordY:%.2f",point.x,point.y); point=[[CCDirectorsharedDirector]convertToGL:point]; CCLOG(@"OpenGLcoordX:%.2fcoordY:%.2f",point.x,point.y); point=[selfconvertToNodeSpace:point]; CCLOG(@"CCNode1coordX:%.2fcoordY:%.2f",point.x,point.y); point=[selfconvertTouchToNodeSpace:touch]; CCLOG(@"CCNode2coordX:%.2fcoordY:%.2f",point.x,point.y); point=[[CCDirectorsharedDirector]convertToUI:point]; CCLOG(@"UIViewcoordX:%.2fcoordY:%.2f",point.x,point.y); point=[rectSpriteconvertTouchToNodeSpaceAR:touch]; CCLOG(@"TouchARcoordX:%.2fcoordY:%.2f",point.x,point.y); returnYES;CCNode } -(void)ccTouchMoved:(UITouch*)touchwithEvent:(UIEvent*)event{ } -(void)ccTouchEnded:(UITouch*)touchwithEvent:(UIEvent*)event{ }
上面的代码添加了UIView的事件处理。由于屏幕和Cocos2D采用不同的坐标系,所以我们需要进行坐标转换。
1)首先获取屏幕坐标
2)将屏幕坐标转换为OpenGL的坐标
3)将OpenGL的坐标转换为Cocos2D的坐标。
从运行结果可以看出Node1和Node2打印出的坐标相同。因为Cocos2D给我们写了covertTouchToNodeSpace方法,可以看看它的源码:
-(CGPoint)convertTouchToNodeSpace:(UITouch*)touch { CGPointpoint=[touchlocationInView:[touchview]]; point=[[CCDirectorsharedDirector]convertToGL:point]; return[selfconvertToNodeSpace:point]; }
至于为什么OpenGL和Node1输出既然相同,为什么还要使用convertToNodeSpace,由于能力有限,希望大牛们能给告诉我答案。
UIView输出的是将Cocos2D坐标转换为屏幕即quartz坐标。
4)convertTouchToNodeSpaceAR是将触摸点转换为相对于rectSprite的坐标
8、判断触摸点是否在制定的精灵上
-(BOOL)ccTouchBegan:(UITouch*)touchwithEvent:(UIEvent*)event{ CGPointpoint=[touchlocationInView:[touchview]]; CCLOG(@"ScreencoordX:%.2fcoordY:%.2f",point.x,point.y); point=[[CCDirectorsharedDirector]convertToGL:point]; CCLOG(@"OpenGLcoordX:%.2fcoordY:%.2f",point.x,point.y); point=[selfconvertToNodeSpace:point]; CCLOG(@"CCNode1coordX:%.2fcoordY:%.2f",point.x,point.y); point=[selfconvertTouchToNodeSpace:touch]; CCLOG(@"CCNode2coordX:%.2fcoordY:%.2f",point.x,point.y); //point=[[CCDirectorsharedDirector]convertToUI:point]; //CCLOG(@"UIViewcoordX:%.2fcoordY:%.2f",point.x,point.y); CCLOG(@"%d",rectSprite.textureRect.size.width); CGRectrect=[rectSpritetextureRect]; rect=CGRectMake(0,0,rect.size.width,rect.size.height); CCLOG(@"orgX:%.2f orgY:%.2f width:%.2f height:%.2f",rect.origin.x,rect.origin.y,rect.size.width,rect.size.height); point=[rectSpriteconvertTouchToNodeSpaceAR:touch]; CCLOG(@"TouchARcoordX:%.2fcoordY:%.2f",point.x,point.y); if(CGRectContainsPoint(rect,point)){ CCLOG(@"YoutouchedintherectSprite"); } returnYES; }
9、获取触摸的是哪个tile,并改变该tile.
CCSprite*rectSprite; CCTMXTiledMap*gameWorld; intspeed=10; -(id)init { //alwayscall"super"init //Applerecommendstore-assign"self"withthe"super"returnvalue if((self=[superinit])){ gameWorld=[CCTMXTiledMaptiledMapWithTMXFile:@"PositionText.tmx"]; [selfaddChild:gameWorld]; CGSizewinSize=[[CCDirectorsharedDirector]winSize]; CCLOG(@"gameWorld.mapSize.width:%.2f gameWorld.mapSize.height:%.2f",gameWorld.mapSize.width,gameWorld.mapSize.height); CCLOG(@"gameWorld.tileSize.width:%.2f gameWorld.tileSize.height:%.2f",gameWorld.tileSize.width,gameWorld.tileSize.height); CCLOG(@"winSize.width:%.2f winSize.height:%.2f",winSize.width,winSize.height); CCSprite*pointSprite=[CCSpritespriteWithFile:@"point.png"]; [gameWorldaddChild:pointSpritez:2tag:1]; pointSprite.position=ccp(20,20); pointSprite.anchorPoint=ccp(0.5,0.5); rectSprite=[CCSpritespriteWithFile:@"myrect.png"]; [gameWorldaddChild:rectSpritez:0tag:3]; rectSprite.position=ccp(40,40); rectSprite.anchorPoint=ccp(0,0); [selfsetIsTouchEnabled:YES]; } returnself; } -(void)registerWithTouchDispatcher{ [[CCTouchDispatchersharedDispatcher]addTargetedDelegate:selfpriority:INT_MIN+1swallowsTouches:YES]; } -(BOOL)ccTouchBegan:(UITouch*)touchwithEvent:(UIEvent*)event{ CGPointpoint=[touchlocationInView:[touchview]]; point=[selfconvertTouchToNodeSpace:touch]; CCLOG(@"CCNode2coordX:%.2fcoordY:%.2f",point.x,point.y); CGPointtilePos=[selftilePosition:point]; if(tilePos.x==-1||tilePos.y==-1)returnNO; CCTMXLayer*ly=[gameWorldlayerNamed:@"Layer0"]; if([lytileGIDAt:tilePos]!=3){ [lysetTileGID:0at:tilePos]; } returnYES; } -(void)ccTouchMoved:(UITouch*)touchwithEvent:(UIEvent*)event{ } -(void)ccTouchEnded:(UITouch*)touchwithEvent:(UIEvent*)event{ } -(CGPoint)tilePosition:(CGPoint)pos{ CGPointpoint; CCTMXLayer*ly=[gameWorldlayerNamed:@"Layer0"]; if(ly==nil){ CCLOG(@"Error:Layernotfound!"); returnccp(-1,-1); } CGSizelayerSize=[lylayerSize]; CGSizetileSize=[gameWorldtileSize]; intx=pos.x/tileSize.width; inty=layerSize.height-pos.y/tileSize.height; if((x>=0)&&(x<layerSize.width)&&(y>=0)&&(y<layerSize.height)){ point=ccp(x,y); }else{ point=ccp(-1,-1); } if(point.x<0)returnccp(-1,-1); if(point.y<0)returnccp(-1,-1); if(point.x>=layerSize.width)returnccp(-1,-1); if(point.y>=layerSize.height)returnccp(-1,-1); CCLOG(@"%d,%d",x,y); returnpoint; }