侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Cocos2d实现类似Clash of clans的缩放拖拽效果

2022-06-26 星期日 / 0 评论 / 0 点赞 / 60 阅读 / 8847 字

最近开始跳入做游戏的坑了,而且想做一款独立游戏,虽然随时有烂尾的危险,但无妨一试~敬请期待~ 目前正在准备游戏的原型,由于需要设计像COC的缩放场景,作为cocos2d的菜鸟兼ios开发新手,实在

最近开始跳入做游戏的坑了,而且想做一款独立游戏,虽然随时有烂尾的危险,但无妨一试~敬请期待~

目前正在准备游戏的原型,由于需要设计像COC的缩放场景,作为cocos2d的菜鸟兼ios开发新手,实在search遍了也没有像样的cocos2d代码,即使有效果也未如人意。stackoverflow上有老外关于pinch缩放手势的例子,但放到cocos2d环境,需要控制一个sprite或者tilemap的边界,就变得有点复杂了。这个问题实在卡了我好久好久,但通过参考了一些pinch示例以及不断调试anchor point和map position等值,最终解决,实在泪牛满面!

这个例子我使用了tilemap作为缩放的对象,当然需要的话也可以用sprite或其他代替。在使用了cocos2d的模版生成文件后,可以在hellolayer中的构造器里先添加本次需要的变量

@implementation HelloWorldLayer{    CCTMXTiledMap *map;    CGSize winSize;    CGFloat mapWidth;    CGFloat mapHeight;}

map是我们的tilemap对象,我们用它来初始化tile map;winSize包含了屏幕的大小参数;mapWidth和mapHeight是实际制作的tilemap的高宽值。同时在init方法中添加代码:

-(id) init{	if( (self=[super init]) ) {                winSize=[CCDirector sharedDirector].winSize;                //加载地图        map=[[CCTMXTiledMap alloc] initWithTMXFile:@"square_tile.tmx"];        map.anchorPoint=CGPointZero;        map.position=CGPointZero;        map.scale=2;        [self addChild:map];                //除以2是考虑retina,实际中应该对此判断        mapWidth= map.tileSize.width*map.mapSize.width/2.0f;        mapHeight= map.tileSize.height*map.mapSize.height/2.0f;                //缩放处理        UIPinchGestureRecognizer *pinchRecognizer = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchFrom:)] autorelease];        [[[CCDirector sharedDirector] view] addGestureRecognizer:pinchRecognizer];                //滑动        [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];	}	return self;}

这段代码分三步来分解:

1.加载tilemap地图文件,anchor point和position属性值均为CGPointZero,这里为了方便retina显示,初始化scale是2,retina的解决方案暂时不在这里考虑,同时为了简易,相关的参数是用实际数字代替,实际开发可以对各种参数重构。地图的资源文件在本文最后的demo里能够找到~

2.pinch手势的缩放,交由本类的handlePinchFrom方法处理

3.最后是滑动处理,由多触点的方法接口实现,下面将引入ccTouchBegan和ccTouchMoved来控制视图的滑动和边界限制等

NOW LET'S KICK OFF!

我们来编写核心功能缩放zoom的处理代码,需要实现的基本需求有:pinch手势(两指滑动)能对当前视图进行相应的放大缩小;缩放的焦点须要为两指初始位置的中点上;同时视图必须以该焦点作为中心

添加handlePinchFrom方法的具体代码:

-(void) handlePinchFrom:(UIPinchGestureRecognizer *)gestureRecognizer{    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan){                UIView *piece = gestureRecognizer.view;                CGPoint location = [gestureRecognizer locationInView:piece];                location=[[CCDirector sharedDirector] convertToGL:location];                CGPoint locationInView=[map convertToNodeSpace:location];                //ax-新的anchor point中x的值,ay同理        CGFloat ax=locationInView.x / mapWidth;        CGFloat ay=locationInView.y / mapHeight;                CGPoint prevAnchor=map.anchorPoint;                map.anchorPoint = ccp(ax,ay);                //调整后的地图位置        map.position=ccp(mapWidth*ax*map.scale+map.position.x-map.boundingBox.size.width*prevAnchor.x,mapHeight*ay*map.scale+map.position.y -map.boundingBox.size.height*prevAnchor.y);                        gestureRecognizer.scale=map.scale;    }        map.scale=gestureRecognizer.scale;        [self adjustViewBoundingPosition:self.position];}

实际中调试还必须加入以下方法:

-(void)adjustViewBoundingPosition:(CGPoint)newPos{}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{	return YES;}-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{}

现在可以build后看看效果了!能够看到地图已经能根据两点焦点进行缩放,基本上这就是本例的核心功能!但还不能移动,我们后面再解决,先看下handlePinchFrom的方法:)

CGPoint location = [gestureRecognizer locationInView:piece];
这句是根据获取当前pinch手势的焦点,获取该点后,通过[map convertToNodespace:location]将它转换成地图上相对的坐标位置,然后计算出焦点相对地图x轴和y轴的比例,从而获得了地图新的anchor point,我们的地图将以此新的焦点进行缩放。但设置了anchor point,必然需要对position位置值进行相应移动才能保证地图是平滑放大!所以position的计算值才是最难的一点,为此需要记录缩放前的anchor point(prevAnchor)作为参考值计算
map.position=ccp(mapWidth*ax*map.scale+map.position.x-map.boundingBox.size.width*prevAnchor.x,mapHeight*ay*map.scale+map.position.y -map.boundingBox.size.height*prevAnchor.y);
以上就是地图位置的调整公式,通过反复调试得出,数学好的可以想一下哈哈

map.scale=gestureRecognizer.scale;

按当前手势的比例设置地图比例,非常简单,就不用多解释了。如果需要优化的话,可以加入最大值和最小值的参数来限制缩放的比例。

最后的adjustViewBoundingPosition方法是为了让地图的上下左右边界不超出界限,即不会划出屏幕以外,下面我们来填充剩下的adjustViewBoundingPosition和CCTouchMoved方法来实现移动!

-(void)adjustViewBoundingPosition:(CGPoint)newPos{    CGFloat adjustWidth=map.boundingBox.size.width*map.anchorPoint.x-map.position.x;    CGFloat adjustHeight=map.boundingBox.size.height*map.anchorPoint.y-map.position.y;        newPos.x=  MIN(newPos.x, adjustWidth);    newPos.x = MAX(newPos.x, winSize.width-[map boundingBox].size.width+adjustWidth);    newPos.y = MIN(newPos.y,adjustHeight);    newPos.y = MAX(newPos.y, winSize.height-[map boundingBox].size.height+adjustHeight);    self.position=newPos;}-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{	return YES;}-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{    CGPoint touchLocation = [self convertTouchToNodeSpace:touch];        CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];    oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];    oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];        CGPoint translation = ccpSub(touchLocation, oldTouchLocation);    CGPoint newPos= ccpAdd(self.position, translation);        [self adjustViewBoundingPosition:newPos];}

CCTouchMoved的方法通过获得手势滑动前后的位置差来设置新视图的位置,并使用了adjustViewBoundingPosition来限制移动边界。如果需要做到COC效果滑动时带有惯性,则又必须添加几项控制参数(如是否为拖动状态)同时设置一个schedule来添加惯性的动画,本例没有做这个效果,可以另行添加

adjustViewBoundingPosition和常规的控制边界代码没有什么不同,主要是多了adjustHeight和adjustWidth的值,这两个值是根据当前map的anchor point和position决定的。另外MIN和MAX的相关值与缩放的实际需求相关,譬如本人不需要考虑scale<2即地图小于屏幕的情况,所以MIN和MAX是定死了,可根据是否小于屏幕来对调MIN和MAX的值

以上就是基本的代码,由于只考虑实现,所以实际项目中还是需要按自己需求去调整,譬如添加惯性,控制缩放比例,缩放达到界限后用动画过渡等效果,在本demo中都没有提供,但这些处理并不复杂~so~

本例的代码已经放到github上,需要的同学可以下来看看 :) 

Check this out: https://github.com/yijiancen/pinch-and-scroll-like-clash-of-clans



广告 广告

评论区