推荐信息:
财经
频道
您的位置:首页 > 财经 > 热点 > 正文

iOS 动画实战之钓鱼小游戏

2018/1/12 19:00:55 来源:iOS开发 []

前言

最近写了一款钓鱼小游戏,版权huijindi.com自己平时也没做过游戏,本来以为这种游戏要用cocos2d什么的实现,后来发现其实动画就可以实现很棒的效果,先看看效果图。

钓鱼游戏.gif

思维导图

首先我们看下思维导图,本游戏主要分为4大块,其中鱼的实现最为复杂

思维导图

项目结构

准备工作

首先将需要的图准备好,这个鱼其实就是一组图片,图片大小固定,iOS 动画实战之钓鱼小游戏每一帧位置变化,所以看起来 是一个上下游动的鱼。

动态.gif

单张图片

鱼钩模块

  • 摆动动画

鱼钩的摆动范围是[M_PI/4.0,-M_PI/4.0] (垂直向下为0度,顺时针为正),这里利用了计时器进行角度的更改,计时器用的CADisplayLink,它是一个和屏幕刷新率一致的定时器,如果没有卡顿,每秒刷新次数是60次,本Demo很多计时器用的都是CADisplayLink。网站http://www.huijindi.com/下面是鱼钩的主要代码(重点:1、设置锚点后重置frame,2、更改角度,3、旋转)。 其中定义了一个block将角度angle回传到FishingView界面计算鱼钩落到池塘的位置。

@property (nonatomic, strong) CADisplayLink *linkTimer;

@property (nonatomic, assign) BOOL isReduce;//改变方向

@property (nonatomic, assign) CGFloat angle;//摆动的角度

- (void)initView{

[self setAnchorPoint:CGPointMake(0.5, 0) forView:self];

UIImageView *gouImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.frame.size.height - 35 , 30, 35)];

gouImageView.image = [UIImage imageNamed:@"fish_catcher_tong"];

[self addSubview:gouImageView];

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake((self.frame.size.width - 3)/2.0, 0, 3, self.frame.size.height - 35)];

lineView.backgroundColor = HEXCOLOR(0x9e664a);

[self addSubview:lineView];

// 创建一个对象计时器

_linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(hookMove)];

//启动这个link

[_linkTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

}

//设置锚点后重新设置frame

- (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view{

CGRect oldFrame = view.frame;

view.layer.anchorPoint = anchorpoint;

view.frame = oldFrame;

}

#pragma mark - 鱼钩摆动

- (void)hookMove{

if (self.isReduce){

_angle-=1.8*cos(1.5*_angle)*0.01;//计算角度,利用cos模拟上升过程中减慢,下降加快

if (_angle < -M_PI/180*45){

self.isReduce = NO;

}

}else {

_angle+=1.8*cos(1.5*_angle)*0.01;

if (_angle > M_PI/180*45){

self.isReduce = YES;

}

}

if (self.angleBlock){

self.angleBlock(_angle);

}

// DLog(@"鱼钩角度%f",_angle);

//旋转动画

self.transform = CGAffineTransformMakeRotation(_angle);

}

鱼模块

鱼模块是继承自UIImageView的一个类

鱼模块提供了三种初始化方式,可垂钓的鱼、不可垂钓的鱼(可以不用)、钓到的鱼三种鱼。

鱼的移动方式有两种,使用枚举定义,阅读http://www.huijindi.com/从左到右,从右到左

鱼的种类有六种,用枚举进行了定义

typedef NS_ENUM(NSInteger, FishModelImageViewType){

FishModelImageViewTypeXHY = 0, //小黄鱼

FishModelImageViewTypeSBY = 1, //石斑鱼

FishModelImageViewTypeHSY = 2, //红杉鱼

FishModelImageViewTypeBWY = 3, //斑纹鱼

FishModelImageViewTypeSHY = 4, //珊瑚鱼

FishModelImageViewTypeSY = 5, //鲨鱼

};

提供了一个钓到鱼后的代理

FishModelImageViewDelegate

//鱼的种类-游动方向-赢取金额

方法 - (void)catchTheFishWithType:(FishModelImageViewType)type

andDirection:(FishModelImageViewDirection)dir

andWinCount:(int)count;

  • 1、动态的鱼

加载动态鱼的方法

//初始化UIImageView

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 55, 55)];

//如果图片的名字是有顺序的,例如xhy1,xhy2,xhy3...,可以取去掉序号的名字,然后会自动将所有的图片都加载进来,duration是动画时长

imageView.image = [UIImage animatedImageNamed:@"xhy" duration:1];

[self.view addSubview:imageView];

初始化不同的鱼,不同的鱼大小不同,移动的速度不同,所以动画时长不一样

//初始化小鱼 git动画时长

- (void)initViewWithType:(FishModelImageViewType)type andDuration:(double)time{

self.fishType = type;

switch (type) {

case FishModelImageViewTypeXHY://小黄鱼

self.duration = 6.0;

self.frame = CGRectMake(-100, 0, 35, 40); //鱼的大小要定义好

self.image = [UIImage animatedImageNamed:@"xhy" duration:time];

break;

case FishModelImageViewTypeSBY://石斑鱼

self.duration = 7.0;

self.frame = CGRectMake(-100, 0, 50, 50);

self.image = [UIImage animatedImageNamed:@"sby" duration:time];

break;

case FishModelImageViewTypeHSY://红杉鱼

self.duration = 8.0;

self.frame = CGRectMake(-100, 0, 50, 40);

self.image = [UIImage animatedImageNamed:@"hsy" duration:time];

break;

case FishModelImageViewTypeBWY://斑纹鱼

self.duration = 8.5;

self.frame = CGRectMake(-100, 0, 65, 53);

self.image = [UIImage animatedImageNamed:@"bwy" duration:time];

break;

case FishModelImageViewTypeSHY://珊瑚鱼

self.duration = 9.0;

self.frame = CGRectMake(-100, 0, 55, 55);

self.image = [UIImage animatedImageNamed:@"shy" duration:time];

break;

case FishModelImageViewTypeSY://鲨鱼

self.duration = 11.0;

self.frame = CGRectMake(-200, 0, 145, 90);

self.image = [UIImage animatedImageNamed:@"sy" duration:time];

break;

}

}

  • 2、移动的鱼

提供的图片都是头朝左的(见上面的动图),网站http://www.huijindi.com/所以从左往右游的话图片需要进行镜像反转

对于鱼是否可以垂钓是用通知进行传递信息的,可垂钓、不可垂钓两种状态

可垂钓:鱼钩沉到鱼塘时受到垂钓通知(将鱼钩底部的坐标传过来),现在鱼可以垂钓,当根据上钩概率等因素判断鱼上钩后,对鱼进行旋转,然后执行上钩动画。动画结束后执行代理。

//初始化可以垂钓的鱼

- (instancetype)initCanCatchFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir{

if (self = [super init]){

self.direction = dir;

[self initViewWithType:type andDuration:1];

if (dir == FishModelImageViewFromLeft){//从左往右,默认所有的鱼都是从右往左

self.transform = CGAffineTransformMakeScale(-1, 1); //镜像

}

[self initFishView];

}

return self;

}

#pragma mark - 可以垂钓的鱼(计时器)

- (void)initFishView{

//接收可以垂钓的通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCanCatch:) name:NotificationFishHookStop object:nil];

//接收不可垂钓的通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCannotCatch) name:NotificationFishHookMove object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeTimer) name:NotificationRemoveFishModelTimer object:nil];

//创建计时器

_linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(fishMove)];

//启动这个link(加入到线程池)

[_linkTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

_offsetX = ScreenWidth;

_offsetY = 100;

_fishWidth = self.frame.size.width;

//Y可变高度范围

_randomRange = (int) (YuTangHeight - self.frame.size.height - OffSetYRange);

self.speed = (ScreenWidth + _fishWidth)/self.duration;//游动速度

self.changeX = self.speed/60.0;//计时器每秒60次

DLog(@"鱼游动的速度:%f,每次位移:%f", self.speed,self.changeX);

}

鱼移动动画和上钩动画

- (void)fishMove{

if (self.direction == FishModelImageViewFromLeft){//从左至右

if (_offsetX > ScreenWidth + _fishWidth){

_offsetY = arc4random()%_randomRange + OffSetYRange;

_offsetX = - _fishWidth - _offsetY;

}

_offsetX+=self.changeX;

self.frame = [self resetFrameOrigin:CGPointMake(_offsetX, _offsetY)];

if ([self fishCanBeCatchedWithOffsetX:_offsetX + _fishWidth]){

NSLog(@"钓到从左到右的鱼了:%ld",(long)self.fishType);

CGAffineTransform transform = CGAffineTransformIdentity;

transform = CGAffineTransformScale(transform, -1, 1);//镜像

transform = CGAffineTransformRotate(transform, M_PI_2);//旋转90度

self.transform = transform;

self.frame = [self resetFrameOrigin:CGPointMake(ScreenWidth*2, 0)];

[self fishCatchedMoveUpWithOffsetX:_offsetX + _fishWidth];

_offsetX = ScreenWidth + _fishWidth + 1;//重置起点

_linkTimer.paused = YES;//计时器暂停

}

}else {//从右到左

if (_offsetX < -_fishWidth){

_offsetY = arc4random()%_randomRange + OffSetYRange;

_offsetX = ScreenWidth + _offsetY;

}

_offsetX-=self.changeX;

self.frame = [self resetFrameOrigin:CGPointMake(_offsetX, _offsetY)];

if ([self fishCanBeCatchedWithOffsetX:_offsetX]){

NSLog(@"钓到从右到左的鱼了:%ld",(long)self.fishType);

self.transform = CGAffineTransformMakeRotation(M_PI_2);

self.frame = [self resetFrameOrigin:CGPointMake(ScreenWidth*2, 0)];

[self fishCatchedMoveUpWithOffsetX:_offsetX];

_offsetX = -_fishWidth-1;//重置起点

_linkTimer.paused = YES;//计时器暂停

}

}

}

鱼上钩的概率和赢得的金币个数

//鱼是否可以被钓上来(根据概率计算)

- (BOOL)fishCanBeCatchedWithOffsetX:(CGFloat)offsetX{

if (!self.isCanCatch) return NO;

if (fabs(offsetX - self.hookX) > self.changeX/2.0) return NO; //判断是否到达了可以垂钓的点

int random = arc4random()%100; //[0,99]

DLog(@"random:%d", random);

switch (self.fishType) {

case FishModelImageViewTypeXHY://小黄鱼 80% 金币2

if (random < 80){

self.moneyCount = 2;

return YES;

}

break;

case FishModelImageViewTypeSBY://石斑鱼 50% 金币5

if (random < 50) {

self.moneyCount = 5;

return YES;

}

break;

case FishModelImageViewTypeHSY://红杉鱼 30% 金币10

if (random < 30) {

self.moneyCount = 10;

return YES;

}

break;

case FishModelImageViewTypeBWY://斑纹鱼 15% 金币20

if (random < 15) {

self.moneyCount = 20;

return YES;

}

break;

case FishModelImageViewTypeSHY://珊瑚鱼 5% 金币50

if (random < 5) {

self.moneyCount = 50;

return YES;

}

break;

case FishModelImageViewTypeSY://鲨鱼 1% 金币100

if (random < 1) {

self.moneyCount = 100;

return YES;

}

break;

}

self.moneyCount = 0;

return NO;

}

  • 3.被钓到的鱼

初始化被钓到的鱼方法

//初始化钓到的小鱼

- (instancetype)initCatchedFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir{

if (self = [super init]){

self.direction = dir;

[self initViewWithType:type andDuration:0.5];

//重制x,y坐标,版权huijindi.com 30为鱼钩的宽度,85为鱼钩的长度

self.x = (30 - self.width)/2.0;

self.y = 85 - 6;

if (dir == FishModelImageViewFromLeft){//从左往右,默认所有的鱼都是从右往左

CGAffineTransform transform = CGAffineTransformIdentity;

transform = CGAffineTransformScale(transform, -1, 1);//镜像

transform = CGAffineTransformRotate(transform, M_PI_2);//旋转90度

self.transform = transform;

}else {

self.transform = CGAffineTransformMakeRotation(M_PI_2);

}

}

return self;

}

当鱼被抓到后,执行上钩动画

//鱼被抓到后往上游

- (void)fishCatchedMoveUpWithOffsetX:(CGFloat) offsetX{

//钩沉到鱼塘的高度为45

//位移动画

CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"position"];

ani.duration = 0.7;

if (self.fishType == FishModelImageViewTypeSY){//鲨鱼由于太长,所以不进行上游动画了

ani.fromValue = [NSValue valueWithCGPoint:CGPointMake(offsetX,45 + _fishWidth/2.0)];

ani.toValue = [NSValue valueWithCGPoint:CGPointMake(_hookX, 45 + _fishWidth/2.0)];

}else {

ani.fromValue = [NSValue valueWithCGPoint:CGPointMake(offsetX, (_offsetY < 60) ? 45 + _fishWidth/2.0 : _offsetY)];//离钩子近的话则不进行动画

ani.toValue = [NSValue valueWithCGPoint:CGPointMake(_hookX, 45 + _fishWidth/2.0)];

}

ani.delegate = self;

//设置这两句动画结束会停止在结束位置

[ani setValue:kFishCatchedMoveUpValue forKey:kFishCatchedMoveUpKey];

[self.layer addAnimation:ani forKey:kFishCatchedMoveUpKey];

}

鱼上游动画结束后将翻转的鱼复位,然后执行代理将钓到的鱼通过代理传递出去

#pragma mark - CAAnimationDelegate

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{

if (flag){

if ([[anim valueForKey:kFishCatchedMoveUpKey] isEqualToString:kFishCatchedMoveUpValue]){//鱼上游

if (self.direction == FishModelImageViewFromLeft){

CGAffineTransform transform = CGAffineTransformIdentity;

transform = CGAffineTransformScale(transform, -1, 1);//镜像

transform = CGAffineTransformRotate(transform, 0);//旋转90度

self.transform = transform;

}else {

self.transform = CGAffineTransformMakeRotation(0);

}

if ([self.delegate respondsToSelector:@selector(catchTheFishWithType:andDirection:andWinCount:)]){

[self.delegate catchTheFishWithType:self.fishType andDirection:self.direction andWinCount:self.moneyCount];

}

}

}

}

钓鱼View

这是实现界面了,本来是写在VC里的,后来发现也能提取出来,所有就提取出来了,在调用时非常简单,像正常View一样初始化后添加到主View上即可,在viewDidDisappear中讲资源释放掉即可。

- (void)viewDidLoad {

[super viewDidLoad];

_fishView = [[FishingView alloc] initWithFrame:self.view.bounds];

[self.view addSubview:_fishView];

}

- (void)viewDidDisappear:(BOOL)animated{

[super viewWillDisappear:animated];

[_fishView removeFishViewResource];

}

  • 1.初始化鱼钩

初始化鱼钩

讲鱼钩摆动的角度通过代理传到本界面

#pragma mark - 鱼钩

- (void)initHookView{

_fishHookView = [[FishHookView alloc] initWithFrame:CGRectMake((ScreenWidth - 30)/2.0, 5, 30, 85)];

__weak typeof (self) weakSelf = self;

_fishHookView.angleBlock = ^(CGFloat angle) {

weakSelf.angle = angle;

};

[self addSubview:_fishHookView];

UIImageView *yuGanImageView = [[UIImageView alloc] initWithFrame:CGRectMake(ScreenWidth/2.0 - 2, 0, ScreenWidth/2.0, 50)];

yuGanImageView.image = [UIImage imageNamed:@"fish_gan_tong"];

[self addSubview:yuGanImageView];

}

下钩动画:鱼塘增加了点击手势,点击后执行钓鱼动作,暂停鱼钩摆动计时器,下钩动画结束后发送通知高速鱼模块可以上钩了,并将鱼钩的底部中心坐标传递过去,鱼线用CAShapeLayer绘制,并执行strokeEnd动画

//钓鱼动作

- (void)fishBtnAction{

if (self.fishHookState != FishHookStateShake) return; //不是摇摆状态不可出杆

[self.fishHookView hookTimerPause];//暂停鱼钩的计时器

double degree = _angle*180/M_PI;//度数

double rate = tan(_angle);//比列

DLog(@"degree:%f---rate:%f",degree,rate);

//计算出来线终点x的位置 , 钩到水里的深度不变,即y是固定的

_lineOffsetX = ScreenWidth/2.0 - (FishLineHeigth)*rate;

//钩子底部xy值

_hookBottomX = ScreenWidth/2.0 - (FishLineHeigth + FishHookHeight)*rate;

_hookBottomY = FishLineHeigth + FishHookHeight;

//动画时间

double aniDuration = [self hookOutOfRiver] ? 0.5 : 1;

//绘制路径

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(ScreenWidth/2.0 ,5)];

[path addLineToPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];

//图形设置

_linePathLayer = [CAShapeLayer layer];

_linePathLayer.frame = self.bounds;

_linePathLayer.path = path.CGPath;

_linePathLayer.strokeColor = [HEXCOLOR(0x9e664a) CGColor];

_linePathLayer.fillColor = nil;

_linePathLayer.lineWidth = 3.0f;

_linePathLayer.lineJoin = kCALineJoinBevel;

[self.layer addSublayer:_linePathLayer];

//下钩动画

CAKeyframeAnimation *ani = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"];

ani.duration = aniDuration;

ani.values = @[@0,@0.8,@1];

ani.keyTimes = @[@0,@0.6,@1];

ani.delegate = self;

[ani setValue:kLineDownAnimationValue forKey:kLineDownAnimationKey];

[_linePathLayer addAnimation:ani forKey:kLineDownAnimationKey];

//位移动画

_hookAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

//移动路径

CGFloat tempOffsetX = ScreenWidth/2.0 - (FishLineHeigth*0.8)*rate;

NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(ScreenWidth/2.0 ,5)];

NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(tempOffsetX, FishLineHeigth*0.8)];

NSValue *p3 = [NSValue valueWithCGPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];

_hookAnimation.duration = aniDuration;

_hookAnimation.values = @[p1,p2,p3];

_hookAnimation.keyTimes = @[@0,@0.7,@1];//动画分段时间

//设置这两句动画结束会停止在结束位置

_hookAnimation.removedOnCompletion = NO;

_hookAnimation.fillMode=kCAFillModeForwards;

[_fishHookView.layer addAnimation:_hookAnimation forKey:@"goukey"];

}

钓鱼动作:下钩动画结束后计时器打开,执行此方法;倒计时为最后一秒时鱼不可上钩(鱼上钩动画0.7s,要留上钩动画的时间);计时器为0时发送不可垂钓通知告诉鱼模块不可上钩了,并执行上钩动画。

//钩子停在底部

- (void)hookStop:(NSTimer *)timer{

_stopDuration-=1;

//最后一秒不可上钩

if (_stopDuration == 1){

//发送不可垂钓的通知

self.fishHookState = FishHookStateUp;

[[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookMove object:nil];

}

if (_stopDuration <= 0){

//关闭计时器

[timer setFireDate:[NSDate distantFuture]];

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];

[path addLineToPoint:CGPointMake(ScreenWidth/2.0 ,5)];

_linePathLayer.path = path.CGPath;

//动画时间

double aniDuration = [self hookOutOfRiver] ? 0.5 : 1;

//上钩

CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeStart"];

ani.duration = aniDuration;

ani.fromValue = [NSNumber numberWithFloat:0];

ani.toValue = [NSNumber numberWithFloat:1];

ani.delegate = self;

ani.removedOnCompletion = NO;

ani.fillMode=kCAFillModeForwards;

[ani setValue:kLineUpAnimationValue forKey:kLineUpAnimationKey];

[_linePathLayer addAnimation:ani forKey:kLineUpAnimationKey];

[_fishHookView.layer removeAllAnimations];

NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(ScreenWidth/2.0 ,5)];

NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];

_hookAnimation.duration = aniDuration;

_hookAnimation.values = @[p2,p1];

_hookAnimation.keyTimes = @[@0,@1];

[_fishHookView.layer addAnimation:_hookAnimation forKey:@"goukey"];

}

}

金币动画&加分动画

下钩动画开始,总金币减少10个

上钩动画开始,发送不可垂钓通知,鱼钩状态为上钩状态

如果有捉到鱼(根据鱼模块代理是否执行判断是否捉到),执行金币动画和加分动画

下钩动画结束,发送可以垂钓的通知给鱼模块,并将鱼钩坐标传递过去,开启上钩的计时器

上钩动画结束,更改鱼钩状态,移除一些View,鱼钩继续摆动

#pragma mark - CAAnimationDelegate 动画代理

//动画开始

- (void)animationDidStart:(CAAnimation *)anim{

//下钩动画开始

if ([[anim valueForKey:kLineDownAnimationKey] isEqualToString:kLineDownAnimationValue]){

self.fishHookState = FishHookStateDown;//下钩状态

//钱数

self.moneyLabel.text = [NSString stringWithFormat:@"%d", _totalMoney-=10];

self.winMoney = 0;

}else if ([[anim valueForKey:kLineUpAnimationKey] isEqualToString:kLineUpAnimationValue]){//上钩动画开始

self.fishHookState = FishHookStateUp;//上钩状态

[[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookMove object:nil];

}

if (self.isCatched){//钓到鱼后落金币

HHShootButton *button = [[HHShootButton alloc] initWithFrame:CGRectMake(_lineOffsetX, 0, 10, 10) andEndPoint:CGPointMake(10, 200)];

button.setting.iconImage = [UIImage imageNamed:@"coin"];

button.setting.animationType = ShootButtonAnimationTypeLine;

[self.bgImageView addSubview:button];

[self bringSubviewToFront:button];

[button startAnimation];

HHWinMoneyLabel *winLabel = [[HHWinMoneyLabel alloc] initWithFrame:CGRectMake(_lineOffsetX - 100/2, ScreenFullHeight - FishSeaHeight, 100, 30)];

winLabel.text = [NSString stringWithFormat:@"+%d",_winMoney];

[self addSubview:winLabel];

self.isCatched = !self.isCatched;

//金币总数

self.moneyLabel.text = [NSString stringWithFormat:@"%d", _totalMoney+=self.winMoney];

}

}

//动画结束

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{

if (flag){

if ([[anim valueForKey:kLineDownAnimationKey] isEqualToString:kLineDownAnimationValue]){//下钩动画结束

self.fishHookState = FishHookStateStop;//垂钓状态

//钩的位置

NSDictionary *dic = @{@"offsetX":[NSString stringWithFormat:@"%.2f",_hookBottomX],@"offsetY":[NSString stringWithFormat:@"%.2f",_hookBottomY]};

//发送可以垂钓的通知,钩的位置传过去

[[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookStop object:nil userInfo:dic];

_stopDuration = [self hookOutOfRiver] ? 1 : arc4random()%3 + 3; //默认时间[3,5),抛到岸上1s

//开启上钩定时器

[_fishTimer setFireDate:[NSDate distantPast]];

}else if ([[anim valueForKey:kLineUpAnimationKey] isEqualToString:kLineUpAnimationValue]){//上钩动画结束

self.fishHookState = FishHookStateShake;//摇摆状态

[_linePathLayer removeFromSuperlayer];

[_fishHookView hoolTimerGoOn];//鱼钩计时器继续

_catchedHeight = 0;

//移除钓上来的鱼

[self removeTheCatchedFishes];

}

}

}

鱼模块的代理方法

创建一个被钓到的鱼,加在鱼钩上,这样便可和鱼钩一起执行上钩动画了

#pragma mark - FishModelImageViewDelegate 钓到鱼后的代理

- (void)catchTheFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir andWinCount:(int)count{

self.isCatched = YES;

FishModelImageView *fishImageView = [[FishModelImageView alloc] initCatchedFishWithType:type andDirection:dir];

[self.fishHookView addSubview:fishImageView];

fishImageView.y = fishImageView.y + _catchedHeight;

_catchedHeight += 8;//每钓到一个y坐标往下移

//赢得钱数

self.winMoney += count;

}

  • 2.初始化鱼塘

简单的创建鱼背景并添加点击手势

  • 3.初始化鱼

通过for循环可以创建出多个某种鱼

//小黄鱼

for (int i = 0; i < 8; i++){

FishModelImageView *model1 = [[FishModelImageView alloc] initCanCatchFishWithType:FishModelImageViewTypeXHY andDirection: (i%2 == 0) ? FishModelImageViewFromRight : FishModelImageViewFromLeft];

model1.delegate = self;

[self.bgImageView addSubview:model1];

}

  • 4.资源移除

由于计时器不销毁会造成循环引用,导致内存泄漏,所以必须手动移除他,还有动画如果执行了代理,并且设置了结束后停留在结束位置,也会得不到释放,所以都要手动释放资源

- (void)removeFishViewResource{

//解决鱼钩上钩动画循环引用的问题

_linePathLayer = nil;

//钓鱼计时器关闭

[_fishTimer invalidate];

_fishTimer = nil;

//释放鱼钩的计时器

[self.fishHookView hoolTimerInvalidate];

//发送通知释放小鱼资源

[[NSNotificationCenter defaultCenter] postNotificationName:NotificationRemoveFishModelTimer object:nil];

}

总结

至此,本游戏已经完成了,写的比较多,也比较乱,有什么不好的地方欢迎批评指正,希望对大伙有所帮助吧,本demo地址【https://github.com/Ccalary/FishingGame】

  • 来自: calary

  • 链接:http://www.jianshu.com/p/e6d102a46876

  • iOS开发整理发布,转载请联系作者授权

通过键盘前后键←→可实现翻页阅读

动漫娱乐教育美食国际历史军事推荐

  • 【名侦探柯南零之执行人百度云】名侦探柯南零之执行人高清资源 - 爱推游戏网

    2018最新名侦探柯南零之执行人登场,这次安室透是妥妥的主角,新兰、小五郎和妃英理夫妇也都会出现,想看这部“年货”剧场版欢迎来爱推游戏网下载名侦探柯南零之执行人高清资源观看哟。名侦探柯南零之执行人剧场版资源被观众称为“年货”的《名侦探柯南》剧场版保持着雷打不动的一年一部的效率,虽然近几年来剧场版风格明显跑偏,更像是动作电影,但这丝毫不影响粉丝们的支持,毕竟22年来每逢4月观看柯南剧场版已经成为了习惯。在新剧场版中,发生大规模爆炸事件后,柯南与公安警察产生正面冲突。毛利小五郎被当成是爆炸事件的嫌疑

  • 人性恐怖漫画:我一定会追到你的!!

    作者:潇潇潇潇如来源:自来水之污更多恐怖漫画请关注微信公众号睡前惊悚故事回复关键字漫画目录即可看到目前已更新500+篇恐怖漫画

  • 蜡笔小新中最神秘的人物,就连小新都好奇他的模样去暗中观察

  • 无声恐怖漫画:虐待兔子的坏小孩

    作者:陈庆星来源:黑箱子无声惊恐剧情梳理:其实坏小孩一点都不坏,孩子之所以不停的打兔子并让兔子走是因为他的父母晚上打算炖了它罢了..而兔子并不知道..忠言逆耳利于行,相信大家每个人身边都有一个这样的人,请珍惜你身边对你如实提出意见的朋友,虽然有些话不打中听,但他们是真的为了你好..更多恐怖漫画请关注微信公众号睡前惊悚故事回复关键字漫画目录即可看到目前已更新500+篇恐怖漫画

  • 搞笑漫画:分辨双胞胎女朋友

    那么问题来了,双胞胎的女朋友喝的是什么酒?

  • 【荐读】这幅父子漫画,刷爆了朋友圈!

    如果有一天,孩子问你为什么别人都闯红灯,我们却要等着?为什么餐厅里别人吃饭那么大声说话,我们却总是很小声?一个人是不是越有钱就越了不起?到底什么叫做内在美?读书究竟有没有用?到底什么样子才叫爱国?……这篇文章,值得给所有家长看看!这组名叫《爸爸说》的漫画里,爸爸的回答将会让很多人深思:关于成长里所遇到的那些问题这才是我见过最接地气的回答建议您看完了之后让更多的家长们看看,感恩您的弘扬!来源:文谈旧事

  • 关于流感,还有比这更生动的科普文么?看完已笑趴……

    用漫画告诉你流感是什么!

  • (小视频)来手工活之家参观考察,将珠绣艺术带回家 足不出户的好项目 为家人找到在家做的手工活

    建议WiFi下观看!版权所有:转载时必须以链接形式注明作者和原始出处及本声明。

  • 《宠物小精灵》里八只“失去视力”的神奇宝贝,最后一只最野蛮!

    《宠物小精灵》里神奇宝贝目前共807只,关都、城都和丰缘等七大地区各栖息着不同品种的神奇宝贝,部分地区神奇宝贝的身体构造会因特殊的生存环境而发生改变,其中就包含题主所说的「八只看不见的神奇宝贝」。黑暗中的超声波源于现实的设计超音蝠的的设计理念源于现实中的蝙蝠,因为现实中蝙蝠不喜欢阳光并常年栖息在阴暗的山洞中,所以蝙蝠眼睛的视力退化,在黑暗的环境中以声波代替眼睛探索周围环境。没有眼睛的原因由于人们普遍认为蝙蝠的视力很差,所以当初设计者没有给超音蝠设计眼睛。行动方式超音蝠的行动方式与现实中的蝙蝠无异

  • 我有故事,你有泡面吗?