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

接触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; 
}

(0)

相关推荐

  • Cocos2d-x中调用Lua及HelloWorld.lua源码分解

    Lua脚本,以前我不知道它有多强大,但是当我做了1年的手机网游之后,起码我发现了,更新客户端是一件很恐怖的事情(会导致大量玩家的流失,以及有一个漫长并且惊心动魄的审核过程),太扯了.于是,如果可以只更新脚本,那就不需要玩家重新下载客户端了,直接在线更新,嗯嗯,虽然我发现了这个,但是还没有实现.噗,代码写烂了,不好改.   所以啊,我已经做好准备了,如果下次用cocos2d-x开发,lua不可少啊~!   看看官方的例子吧,先来AppDelegate.cpp的applicationDidFinis

  • Cocos2d-x中使用CCScrollView来实现关卡选择实例

    类似关卡选择的这种功能游戏中经常看到,比如帮助场景,选择关卡,通过滑动的方式选择一些其他的东西等等.今天我们实现关卡的选择是使用CCScrollView这个类.当然还有一些其他的方法,比如使用cocostudio的page view也可以.我先说下整体的思路,CCScrollView这个类是继承自CCLayer的,本身的触摸事件有些bug,所以网上一般将这个层的touch事件处理为false,而使用它的父节点来处理触摸事件,我们也是采用这个做法.先定义一个LevelScene类,将CCScrol

  • 剖析iOS开发中Cocos2d-x的内存管理相关操作

    一,IOS与图片内存 在IOS上,图片会被自动缩放到2的N次方大小.比如一张1024*1025的图片,占用的内存与一张1024*2048的图片是一致的.图片占用内存大小的计算的公式是:长*宽*4.这样一张512*512 占用的内存就是 512*512*4 = 1M.其他尺寸以此类推.(ps:IOS上支持的最大尺寸为2048*2048). 二,cocos2d-x 的图片缓存 Cocos2d-x 在构造一个精灵的时候会使用spriteWithFile或者spriteWithSpriteFrameNa

  • iOS开发中使用cocos2d添加触摸事件的方法

    CCLayer类是用来接收触摸输入的.不过你要首先启用这个功能才可以使用它.你通过设置isTouchEnabled为YES来让层接收触摸事件: 复制代码 代码如下: self.isTouchEnabled = YES; 此项设定最好在init方法中设置.你可以在任何时间将其设置为NO或者YES. 一旦启用isTouchEnabled属性,许多与接收触摸输入相关的方法将会开始被调用.这些事件包括:当新的触摸开始的时候,当手指在触摸屏上移动的时候,还有在用户手指离开屏幕以后.很少会发生触摸事件被取消

  • cocos2dx骨骼动画Armature源码剖析(三)

    cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下.(mac下没有分,是整个一坨) armature(目录): animation(目录):动画控制相关. CCProcessBase(文件): ProcessBase(类):CCTween和ArmatureAnimation的基类. CCTWeen(文件): Tween(类):控制flash里一个layer的动画. CCArmatureAnimat

  • cocos2dx骨骼动画Armature源码剖析(一)

    cocos2dx从编辑器(cocostudio或flash插件dragonBones)得到xml或json数据,调用类似如下所示代码就可以展示出动画效果 ArmatureDataManager::getInstance()->addArmatureFileInfoAsync( "armature/Dragon.png", "armature/Dragon.plist", "armature/Dragon.xml", this, schedu

  • Cocos2d-x 3.x入门教程(二):Node节点类

    千里之行,始于足下 上一篇文章<菜鸟学习Cocos2d-x 3.x--基础概念>中介绍了以下Cocos2d-x的基本结构和一些重要的类,而这里开始对这些重要的类进行单独的介绍与分析.开始吧~~~ Node节点类 Node类是Cocos2d-x中一个非常重要的类,它继承自类Ref,关于Ref类,Ref类是一个内存管理的类,我后续也会总结的,这里就不做多说了. Node类在Cocos2d-x中有多重要呢?任何需要画在屏幕上的对象都是节点类.最常用的节点类包括场景类(Scene).布景层类(Laye

  • Cocos2d-x触摸事件实例

    在玩手机游戏的时候,屏幕接收我们的触摸消息是必不可少的,根据我们的触摸事件,去实现相应的功能,这里我们就来学习一下cocos2d-x中的触摸是怎么实现的.触摸分为单点触摸和多点触摸,先来看单点触摸,就是接收一个点的触摸.代码将实现过程清楚的写了下来,仔细分析代码吧. bool HelloWorld::init() { bool bRet = false; do { CC_BREAK_IF(! CCLayer::init()); //开启触摸 this->setTouchEnabled(true)

  • Cocos2d-x中CCEditBox文本输入框的使用实例

    文本输入框这个东西相信大家不论做什么游戏总会用到吧,今天我们就来看看这个东西如何使用.文本输入框同样属于扩展库中的内容,所以你知道怎么做了吧.当用户要在文本框中输入内容,这一系列的过程我们需要一些函数的调用来获得我们想要的东西,包含这些函数的类需要实现CCEditBoxDelegate这个接口,下面我们来看看具体如何使用吧. #ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h"

  • cocos2dx骨骼动画Armature源码剖析(二)

    上篇文章从总体上介绍了cocos2dx自带的骨骼动画,这篇文章介绍一下导出的配置数据各个字段的含义(也解释了DragonBone导出的xml数据每个字段的含义). skeleton节点 <skeleton name="Dragon" frameRate="24" version="2.2"> name:flash文件名字. frameRate:flash帧率. version:dragonbones版本号. armatures节点 首

  • Cocos2d-x中实现弹出对话框示例

    在游戏中我们经常会看到弹出一个对话框让我们进行选择,今天我们就在cocos2dx中实现这个对话框.对话框说白了也是一个层,当我们点击某一个按钮的时候这个层被加进了当前的场景中,同时场景中的其他的层都是不可点击的,这个时候就涉及到触摸的优先级的一些问题,当然有些时候你也可以根据自己的需要让其他的层也可以点击,但是道理都是一样的,学会了这个其他的按照自己的要求去实现吧.下面我将弹出层单独分装成一个类,供我们调用. /*对话框场景类的头文件*/ #ifndef _POP_SCENE_H_ #defin

  • Cocos2d-x人物动作类实例

    我们玩的游戏一般都可以看到精灵的运动,游戏的世界就是一个运动的世界,而所有的这些动作都可以分为一些基本的动作和动作的组合,今天就来学习一下动作类CCAction,首先看一下类之间的继承关系. CCAction类下派生了三个动作类,执行动作的类是CCNode以及它的子类,通过函数runAction()来执行动作,其中CCFiniteTimeAction之下是常用的瞬时动作和延时动作.动作从本质上来说就是改变节点的属性,瞬时动作就是改变这些属性不需要时间,瞬时就完成了,而延时动作改变这些属性需要一些

  • Cocos2d-x 3.x入门教程(一):基础概念

    前言 接触游戏开发时间不长,之前一直都是写Lua,写Lua肯定是没有什么发展的啦,至少你要会写的.写客户端,肯定要看看Cocos2d-x了.从今天起,正式开始Cocos2d-X的学习之旅. 之前一直都是做C++开发的,转过来看Cocos2d-x,从语言上来说,没有多少问题.对于我来说,Cocos2d-x就是一个类库,就类似于MFC.ATL和QT一样,就是一个游戏类库,而我就按照学习类库使用的方法去学习Cocos2d-x.这篇文章叫基础概念,但是还是从整体来说说Cocos2d-x这个类库的. 基础

随机推荐