详解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;
}