欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解iOS游戏开发中Cocos2D的坐标位置关系

程序员文章站 2023-11-25 11:12:28
接触cocos2d有段时间了,今天特意研究了下cocos2d坐标系中各种位置关系,anchor属性,ccnode坐标和地图坐标转换。    ...

接触cocos2d有段时间了,今天特意研究了下cocos2d坐标系中各种位置关系,anchor属性,ccnode坐标和地图坐标转换。

    先看一段代码:

复制代码 代码如下:

-(id) init 

    // always call "super" init 
    // apple recommends to re-assign "self" with the "super" return value 
    if( (self=[super init])) { 
        cctmxtiledmap *gameworld = [cctmxtiledmap tiledmapwithtmxfile:@"positiontext.tmx"]; 
        [self addchild:gameworld]; 
        cgsize winsize = [[ccdirector shareddirector] 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 = [ccsprite spritewithfile:@"point.png"]; 
        [gameworld addchild:pointsprite z:2 tag:1]; 
        pointsprite.position = ccp(20,20); 
        pointsprite.anchorpoint = ccp(0.5, 0.5); 
         
         
        ccsprite *rectsprite = [ccsprite spritewithfile:@"myrect.png"]; 
        [gameworld addchild:rectsprite z:1 tag:1]; 
        rectsprite.position = ccp(20,20); 
        rectsprite.anchorpoint = ccp(0.5, 0.5);      
    } 
    return self; 
}

1、加载地图:
复制代码 代码如下:

cctmxtiledmap *gameworld = [cctmxtiledmap tiledmapwithtmxfile:@"positiontext.tmx"]; 

2、获取手机屏幕大小

复制代码 代码如下:

cgsize winsize = [[ccdirector shareddirector] 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 

    // always call "super" init 
    // apple recommends to re-assign "self" with the "super" return value 
    if( (self=[super init])) { 
        cctmxtiledmap *gameworld = [cctmxtiledmap tiledmapwithtmxfile:@"positiontext.tmx"]; 
        [self addchild:gameworld]; 
        cgsize winsize = [[ccdirector shareddirector] 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 = [ccsprite spritewithfile:@"point.png"]; 
        [gameworld addchild:pointsprite z:2 tag:1]; 
        pointsprite.position = ccp(20,20); 
        //pointsprite.anchorpoint = ccp(0.5, 0.5); 
         
         
        ccsprite *rectsprite = [ccsprite spritewithfile:@"myrect.png"]; 
        [gameworld addchild:rectsprite z:1 tag:1]; 
        rectsprite.position = ccp(40,40); 
        rectsprite.anchorpoint = ccp(2, 2);  
         
        [self setistouchenabled:yes]; 
    } 
    return self; 

 
- (void) registerwithtouchdispatcher { 
    [[cctouchdispatcher shareddispatcher] addtargeteddelegate:self priority:int_min + 1 swallowstouches:yes]; 

 
- (bool) cctouchbegan:(uitouch *)touch withevent:(uievent *)event { 
    cgpoint point = [touch locationinview: [touch view]]; 
    cclog(@"screen coordx: %.2f coordy: %.2f", point.x, point.y); 
     
    point = [[ccdirector shareddirector] converttogl: point]; 
    cclog(@"opengl coordx: %.2f coordy: %.2f", point.x, point.y); 
 
    point = [self converttonodespace: point]; 
    cclog(@"ccnode1 coordx: %.2f coordy: %.2f", point.x, point.y); 
 
    point = [self converttouchtonodespace: touch]; 
    cclog(@"ccnode2 coordx: %.2f coordy: %.2f", point.x, point.y); 
         
        point = [[ccdirector shareddirector] converttoui:point]; 
        cclog(@"uiview coordx: %.2f coordy: %.2f", point.x, point.y); 
        
        point = [rectsprite converttouchtonodespacear:touch]; 
        cclog(@"touchar coordx: %.2f coordy: %.2f", point.x, point.y); 
        return yes; ccnode 

 
- (void) cctouchmoved:(uitouch *)touch withevent:(uievent*)event { 
     

 
- (void) cctouchended:(uitouch *)touch withevent:(uievent*)event { 
     


     上面的代码添加了uiview的事件处理。由于屏幕和cocos2d采用不同的坐标系,所以我们需要进行坐标转换。

     1)首先获取屏幕坐标

     2)将屏幕坐标转换为opengl的坐标

     3)将opengl的坐标转换为cocos2d的坐标。

     从运行结果可以看出node1和node2打印出的坐标相同。因为cocos2d给我们写了coverttouchtonodespace方法,可以看看它的源码:

复制代码 代码如下:

- (cgpoint)converttouchtonodespace:(uitouch *)touch 

    cgpoint point = [touch locationinview: [touch view]]; 
    point = [[ccdirector shareddirector] converttogl: point]; 
    return [self converttonodespace:point]; 
}

     至于为什么opengl和node1输出既然相同,为什么还要使用converttonodespace,由于能力有限,希望大牛们能给告诉我答案。
    uiview输出的是将cocos2d坐标转换为屏幕即quartz坐标。
   4) converttouchtonodespacear是将触摸点转换为相对于rectsprite的坐标


8、判断触摸点是否在制定的精灵上

复制代码 代码如下:

- (bool) cctouchbegan:(uitouch *)touch withevent:(uievent *)event { 
    cgpoint point = [touch locationinview: [touch view]]; 
    cclog(@"screen coordx: %.2f coordy: %.2f", point.x, point.y); 
     
    point = [[ccdirector shareddirector] converttogl: point]; 
    cclog(@"opengl coordx: %.2f coordy: %.2f", point.x, point.y); 
 
    point = [self converttonodespace: point]; 
    cclog(@"ccnode1 coordx: %.2f coordy: %.2f", point.x, point.y); 
 
    point = [self converttouchtonodespace: touch]; 
    cclog(@"ccnode2 coordx: %.2f coordy: %.2f", point.x, point.y); 
     
    //point = [[ccdirector shareddirector] converttoui:point]; 
    //cclog(@"uiview coordx: %.2f coordy: %.2f", point.x, point.y); 
    cclog(@"%d", rectsprite.texturerect.size.width); 
     
    cgrect rect = [rectsprite texturerect]; 
    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 = [rectsprite converttouchtonodespacear:touch]; 
    cclog(@"touchar coordx: %.2f coordy: %.2f", point.x, point.y); 
    if(cgrectcontainspoint(rect, point)) { 
        cclog(@"you touched in the rectsprite"); 
    } 
    return yes;  


9、获取触摸的是哪个tile,并改变该tile.
复制代码 代码如下:

ccsprite *rectsprite; 
cctmxtiledmap *gameworld; 
int speed = 10; 
-(id) init 

    // always call "super" init 
    // apple recommends to re-assign "self" with the "super" return value 
    if( (self=[super init])) { 
        gameworld = [cctmxtiledmap tiledmapwithtmxfile:@"positiontext.tmx"]; 
        [self addchild:gameworld]; 
        cgsize winsize = [[ccdirector shareddirector] 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 = [ccsprite spritewithfile:@"point.png"]; 
        [gameworld addchild:pointsprite z:2 tag:1]; 
        pointsprite.position = ccp(20,20); 
        pointsprite.anchorpoint = ccp(0.5, 0.5); 
         
         
        rectsprite = [ccsprite spritewithfile:@"myrect.png"]; 
        [gameworld addchild:rectsprite z:0 tag: 3]; 
        rectsprite.position = ccp(40, 40); 
        rectsprite.anchorpoint = ccp(0, 0); 
                 
        [self setistouchenabled:yes]; 
    } 
    return self; 

 
- (void) registerwithtouchdispatcher { 
    [[cctouchdispatcher shareddispatcher] addtargeteddelegate:self priority:int_min + 1 swallowstouches:yes]; 

 
- (bool) cctouchbegan:(uitouch *)touch withevent:(uievent *)event { 
    cgpoint point = [touch locationinview: [touch view]]; 
    point = [self converttouchtonodespace: touch]; 
    cclog(@"ccnode2 coordx: %.2f coordy: %.2f", point.x, point.y); 
 
    cgpoint tilepos = [self tileposition:point]; 
    if(tilepos.x == -1 || tilepos.y == -1) return no; 
    cctmxlayer *ly = [gameworld layernamed:@"layer 0"]; 
    if([ly tilegidat:tilepos] != 3) { 
        [ly settilegid:0 at: tilepos]; 
    } 
    return yes;  

 
- (void) cctouchmoved:(uitouch *)touch withevent:(uievent*)event { 
     

 
- (void) cctouchended:(uitouch *)touch withevent:(uievent*)event { 
     

 
 
- (cgpoint) tileposition:(cgpoint)pos { 
    cgpoint point; 
    cctmxlayer *ly = [gameworld layernamed:@"layer 0"]; 
    if(ly== nil) { 
        cclog(@"error: layer not found!"); 
        return ccp(-1, -1); 
    } 
    cgsize layersize = [ly layersize]; 
    cgsize tilesize = [gameworld tilesize]; 
    int x = pos.x / tilesize.width; 
    int y = 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) return ccp(-1, -1); 
    if(point.y < 0) return ccp(-1, -1); 
    if(point.x >= layersize.width) return ccp(-1, -1); 
    if(point.y >= layersize.height) return ccp(-1, -1); 
    cclog(@"%d, %d", x, y); 
    return point;